Constexpr强制转换为const char []

时间:2016-07-03 19:11:44

标签: c++ templates char constexpr

很多人都遇到过这种情况,这件事发生在我身上。我在C ++中使用编译时字符串时遇到了困难。

我决定采用看似无法使用的方法:使用template <char...>类。

这就是我提出的,它是非常常见的,没有什么特别的,它也不起作用。

template <char... chars> class string
{
public:

    static constexpr const char value[] = {chars...};

    constexpr string()
    {
    }

    constexpr operator decltype(value) & () const
    {
        return value;
    }
};

template <char... chars> constexpr const char string <chars...> :: value[];

我的想法是使string实例constexpr可构造并暴露某种constexpr转换,以便它提供其内容。

现在,如果我这样做

static constexpr const char x[] = "ciao";

template <const char * str> void print()
{
    std :: cout << str << std :: endl;
}

print <x> ();

这有效并且说ciao。如果我这样做,我也会ciao

std :: cout << string <'c', 'i', 'a', 'o'> {} << std :: endl;

print <string <'c', 'i', 'a', 'o', '\0'> :: value> ();

但是当我做的时候

print <string <'c', 'i', 'a', 'o', '\0'> {}> ();

我得到:No matching function for call to print

我肯定错过了一些东西。做我想做的事是不可行的吗?让一个实例做一个constexpr演员以某种方式返回value?如果这样可行,我将能够在编译时轻松地进行操作符和字符串操作,仅使用#34;缺点是超级无聊'i', 'n', 'i', 't', 'i', 'a', 'l', 'i', 'z', 'a', 't', 'i', 'o', 'n'

进一步的实验

我做了另一个完美的实验。

template <char... chars> class string
{
public:
   constexpr string()
   {
   }

   constexpr operator size_t () const
   {
      return sizeof...(chars);
   }
};

template <size_t length> void print()
{
   std :: cout << length << std :: endl;
}

print <string <'c', 'i', 'a', 'o'> {}> ();

它打印出漂亮的4

2 个答案:

答案 0 :(得分:4)

在C ++ 14中,对模板非类型参数的模板参数的限制是:

  

非类型非模板模板参数 template-argument 应为以下之一:
  *对于整数或枚举类型的非类型模板参数 template-parameter 类型的转换常量表达式(5.19);或
  *非类型模板参数的名称;或
  *一个常量表达式(5.19),用于指定具有静态存储持续时间和外部或内部链接的完整对象的地址,或具有外部或内部链接的函数,包括函数模板和函数 template -ids 但不包括非静态类成员,表示(忽略括号)为& id-expression ,其中 id-expression 是对象或函数的名称,除了如果名称引用函数或数组,则&可以省略;如果相应的 template-parameter 是引用,则应省略std::nullptr_t;或
  *一个常量表达式,其值为空指针值(4.10);或
  *一个常量表达式,计算结果为空成员指针值(4.11);或
  *指向成员的指针,如5.3.1所述;或
  *类型string<'c', 'i', 'a', 'o', '\0'>{}的常量表达式。

在您的示例中,const char*不是那些。请注意,第一个项目符号限制为整数或枚举类型,print<string<'c', 'i', 'a', 'o', '\0'>::value>(); 不是那些(整数类型的例外是允许编译“进一步实验”的例子)。没有其他子弹适用。因此,符合标准的C ++ 14 应该拒绝您的代码。

解决方法是直接传递底层字符数组:

::value

或者将类型本身作为模板 type 参数,并在函数中打印template <class T> void print() { std::cout << T::value << std::endl; } print<string<'c', 'i', 'a', 'o', '\0'>>();

template<int *p> struct A {};
int n;
A<&n> a; // ok

constexpr int *p() { return &n; }
A<p()> b; // error

或者...

你会很高兴知道在C ++ 17中,事情变得更好。请参阅N4198,并附上激励示例:

typeid

该论文提出了对模板非类型参数的更少限制,这将使上述示例格式良好。新的措辞如下:

  

非类型模板参数 template-argument 应该是template-parameter类型的转换常量表达式(5.20)。对于引用类型或指针类型的非类型模板参数,常量表达式的值不应引用(或者对于指针类型,不应是地址):
  *子对象(1.8),
  *临时对象(12.2),
  *字符串文字(2.14.5),
  * __func__表达式(5.2.8)的结果,或   *预定义的string<'c', 'i', 'a', 'o', '\0'>{}变量(8.4.1)。

就是这样。其他一切都是允许的。

由于for (int i = 0, j = nvr % nr; i < nr; i++) { array[i] = nepr + (i < j); } 转换为不是那些东西的指针,因此该示例变得格式正确。 Clang 3.8将以c ++ 1z模式(正确)编译您的示例,但不能以c ++ 14模式(正确)编译。 GCC尚未实现此目的。请给他们时间。

答案 1 :(得分:-1)

用户定义的转换不考虑模板参数匹配。

然而,而不是

print <string <'c', 'i', 'a', 'o', '\0'>{} > ();

...你可以写

print <string <'c', 'i', 'a', 'o', '\0'>::value> ();

或者你可以定义

template <const char* str>
void print()
{
    std::cout << str << std::endl;
}

template< class Type >
void print()
{
    print<Type::value>();
}

...然后再写

print <x> ();
print <string <'c', 'i', 'a', 'o', '\0'>> ();

...其中x是您的命名空间范围

static constexpr const char x[] = "ciao";

注意:此代码使用-std=c++14编译MinGW g ++ 5.1.0,但不会编译Visual C ++ 2015更新2.这些差异对于推动语言边界的代码很常见。该标准不断变化,编译器正在不断发展以试图跟上标准,因此这些代码在实践中并不是非常便携。