c++ - 在编译时确定参数的唯一性

标签 c++ templates c++14 variadic-templates template-meta-programming

问题

我有以下类似容器的数组:

template <
    class _Tp,
    size_t _Size
> class Alphabet {
public:
    template <class... _Ts>
    constexpr
    Alphabet(_Ts&& ... ts) 
        : m_data(std::forward<_Ts>(ts)...})
    {}
private:
    _Tp m_data[_Size ? _Size : 1];
}

我是这样使用的:

constexpr Alphabet<char, 3> abc{'a', 'b', 'c'}; // ok, elements are distinct

但是,我希望能够在编译时判断元素是否唯一。所以在构造函数的主体中,我只是简单地添加:

Alphabet(_Ts&& ... ts) 
    : m_data{std::forward<_Ts>(ts)...}
{
    static_assert(is_unique(ts...), "Elements must be distinct!")
}

所以现在当我写的时候:

constexpr Alphabet<char, 3> abc{'a', 'b', 'b'}; // error! elements are not distinct

我尝试过的

为此,我编写了以下在运行时运行的代码:

template <class _Tp>
constexpr
bool is_one_of(_Tp)
{
    return false;
}

template <class _Tp, class... _Ts>
constexpr
bool is_one_of(_Tp t, _Tp f, _Ts... ts)
{
    return (t == f) || is_one_of(f, ts...);
}

template <class _Tp>
constexpr
bool is_unique(_Tp)
{
    return true;
}

template <class _Tp, class... _Ts>
constexpr
bool is_unique(_Tp t, _Ts... ts)
{   
    return is_unique(ts...) && !is_one_of(t, ts...);
}

不幸的是,在尝试编译时我很快遇到了以下错误:

error: non-constant condition for static assertion
     static_assert(detail::is_unique(ts...),
                                     ^
error: 'ts#0' is not a constant expression

我在使用 C++14。感谢您的帮助!

最佳答案

如果你想检查ts...值编译时 ( static_assert() ) 您必须将它们作为编译时已知的曾经值传递。

您无法在编译时检查 constexpr 的参数构造函数,因为构造函数也可以与运行时值一起使用。

我的建议是通过 ts...值作为模板参数,因此它们曾经在编译时已知,因此您可以在 static_assert() 中检查它们测试。

例如:如果Tp是整数类型,您可以将它们作为 std::integer_sequence 的参数传递

  template <Tp ... Ts>
  constexpr Alphabet (std::integer_sequence<Tp, Ts...> const &)
     : m_data{Ts...}
   { static_assert(    sizeof...(Ts) <= Size
                    && are_unique<Ts...>(), "!" ); }

关于 are_unique() ,我提出了一个递归版本

  // ground case
  template <typename = void>
  static constexpr bool are_unique ()
   { return true; }

  // recursive case
  template <Tp T0, Tp ... Ts>
  static constexpr bool are_unique ()
   { return is_unique<T0, Ts...>() && are_unique<Ts...>(); }

关于is_unique() , 你可以使用 unused 的初始化技巧数组

  template <Tp T0, Tp ... Ts>
  static constexpr bool is_unique ()
   {
     using unused = bool[];

     bool ret = true;

     (void)unused{ false, ret &= T0 != Ts... };

     return ret;
   }

如果你不喜欢,每次都写std::integer_sequence部分,你只能在 make_Alphabet() 中完成一次模板函数

template <typename Tp, Tp ... Ts>
constexpr Alphabet<Tp, sizeof...(Ts)> make_Alphabet ()
 { return { std::integer_sequence<Tp, Ts...>{} }; }

并按如下方式使用

constexpr auto abcd{ make_Alphabet<char, 'a', 'b', 'c', 'd'>() };

下面是一个完整的编译C++14的例子

#include <utility>
#include <iostream>

template <typename Tp, std::size_t Size>
class Alphabet
 {
   private:
      Tp m_data[Size ? Size : 1U];

      template <Tp T0, Tp ... Ts>
      static constexpr bool is_unique ()
       {
         using unused = bool[];

         bool ret = true;

         (void)unused{ false, ret &= T0 != Ts... };

         return ret;
       }

      // ground case
      template <typename = void>
      static constexpr bool are_unique ()
       { return true; }

      // recursive case
      template <Tp T0, Tp ... Ts>
      static constexpr bool are_unique ()
       { return is_unique<T0, Ts...>() && are_unique<Ts...>(); }

   public:
      template <Tp ... Ts>
      constexpr Alphabet (std::integer_sequence<Tp, Ts...> const &)
         : m_data{Ts...}
       { static_assert(    sizeof...(Ts) <= Size
                        && are_unique<Ts...>(), "!" ); }
 };

template <typename Tp, Tp ... Ts>
constexpr Alphabet<Tp, sizeof...(Ts)> make_Alphabet ()
 { return { std::integer_sequence<Tp, Ts...>{} }; }

int main()
 {
   // compilation error (static_assert failed "!")
   //constexpr Alphabet<char, 3>
   //   aba{std::integer_sequence<char, 'a', 'b', 'a'>{}};

   // compile
   constexpr Alphabet<char, 3>
      abc{std::integer_sequence<char, 'a', 'b', 'c'>{}};

   // compilation error (static_assert failed "!")
   //constexpr auto abca{ make_Alphabet<char, 'a', 'b', 'c', 'a'>() };

   // compile
   constexpr auto abcd{ make_Alphabet<char, 'a', 'b', 'c', 'd'>() };
 }

关于c++ - 在编译时确定参数的唯一性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51342549/

相关文章:

c++ - 为不同的特征专门化相同的运算符

c++ - GLSL 将颜色数据从片段着色器发送到顶点着色器似乎总是等于 0

c++ - Boost Graph Library - 来自外部 vector 的权重属性

c++ - 如何确保可变元组中的类型相同?

c++ - 非静态成员函数的 decltype 格式不正确吗?

c++ - 为两种 vector 类型或一种 vector 类型与标量类型之间的二元运算结果确定正确的大小类型

c++ - 将函数参数限制为某些枚举值

c++ - lua的表模拟

c++ - 在 C++ 中使用 std::istringsteam 将字符串转换为 float

c++ - 静态多态和模板容器