在没有constexpr

时间:2015-11-27 12:17:55

标签: c++ templates gcc clang c++14

请考虑以下代码:

struct A {
    constexpr operator int() { return 42; }
};

template <int>
void foo() {}

void bar(A a) {
    foo<a>();
}

int main() {
    foo<A{}>();

    const int i = 42;
    foo<i>();  // (1)

    A a{};

    static_assert(i == a, "");
    bar(a);
    foo<a>();  // error here
}

Clang 3.7与c ++ 14接受此,而gcc 5.2.0与c ++ 14不接受,产生以下消息:

/tmp/gcc-explorer-compiler1151027-68-1f801jf/example.cpp: In function 'int main()':
26 : error: the value of 'a' is not usable in a constant expression
foo<a>();
^
23 : note: 'a' was not declared 'constexpr'
A a{};
^
Compilation failed

按照gcc的建议将a更改为constexpr可修复gcc编译错误,但没有constexpr,哪个编译器正确?

对我来说,似乎a应该是&#34;可用于常量表达&#34;,因为static_assert ceritifies。此外,i可以以相同的方式使用(标记为(1)),以及bar()编译的事实,这也使我认为gcc是错误的。

UPD :报告了针对gcc的错误:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68588

3 个答案:

答案 0 :(得分:5)

[expr.const]/(4.1)允许用户定义的转化,我在[expr.const]/2中没有看到一个可以防止您的表达式成为常量的公告点。实际上,要求非常宽松,将a声明为

A a;

still giving a well-formed program,即使a没有constexpr默认构造函数等,因为转化运算符为constexpr且未评估任何成员。

正如您所看到的那样,GCC是矛盾的,因为它允许a条件中的static_assert而不是模板参数。

答案 1 :(得分:2)

我会说Clang是正确的。

当前C ++草案(n4296)说:

  

14.3.2模板非类型参数[temp.arg.nontype]

     

非类型模板参数的模板参数应为转换后的常量表达式(5.20)   template-parameter的类型

5.20§4说(强调我的):

  

5.20常量表达式[expr.const]

     

...

     

(4)T类型的转换常量表达式是一个表达式,隐式转换为类型T,转换为   表达式是常量表达式,隐式转换序列仅包含

     

(4.1) - 用户定义的转换,...​​

foo<a>(); a中的IFAIK使用constexpr用户定义的转换转换为int,因此 是转换后的常量表达式。

话虽这么说,我们离这里的边缘案例不远,我的建议是:不要在生产代码中使用这样的构造: - )

答案 2 :(得分:-2)

正如@ Jarod42建议a应为constexpr。这是因为模板是在编译时推断出来的,因此非类型参数也必须在编译时可用。 constexpr是一个承诺,它们将在编译时提供。