考虑简单的C ++ 11代码:
template<int N>
struct Foo {};
template <int N>
constexpr int size(const Foo<N>&) { return N; }
template <int N>
void use_size(const Foo<N>& foo) { constexpr int n = size(foo); }
int main()
{
Foo<5> foo;
constexpr int x = size(foo); // works with gcc and clang
// _but_
use_size(foo); // the same statement in the use_size()
// function _only_ works for gcc
}
我可以用g++ -std=c++11 foo.cpp
但是,如果我使用clang ++,clang++ -std=c++11 foo.cpp
我会
foo.cpp:15:28: error: constexpr variable 'n' must be initialized by a constant expression
void use_size(const Foo<N>& foo) { constexpr int n = size(foo); }
~~~~~^~~~
foo.cpp:23:5: note: in instantiation of function template specialization 'use_size<5>' requested here
use_size(foo); // the same statement in the use_size()
^
1 error generated.
(nb:编译器版本。我用g ++版本5.3.1和7.2.1以及clang ++版本3.6.2和5.0.0检查了以前的语句)
我的问题: g ++或clang中的哪一个是对的?有什么问题?
答案 0 :(得分:9)
我的解释是 clang ++是正确的和 g ++过于宽松。
我们可以在标准https://isocpp.org/std/the-standard中找到一个关闭的示例([expr.const]部分,第126页)(草稿可以下载,attention big PDF!)。
constexpr int g(int k) {
constexpr int x = incr(k);
return x;
}
解释说:
错误:incr(k)不是核心常量表达式,因为 k的生命期开始于表达式incr(k)
之外
这正是具有use_size()
参数的foo
函数中发生的情况,即使size()
函数仅使用N
模板参数。
template <int N>
constexpr int size(const Foo<N>&) { return N; }
template <int N>
void use_size(const Foo<N>& foo) { constexpr int n = size(foo); }
答案 1 :(得分:3)
我期待Clang在这种情况下出错。它应该将您的函数调用评估为一个常量表达式,因为您只使用模板参数,而不是对象本身。由于您不在constexpr
函数中使用该对象,因此不应该禁止编译时评估。
然而,标准中有一条规则,即在常量表达式之前开始生命的对象(例如引用)不能用作constexpr。
在这种情况下有一个简单的解决方法。我认为它不喜欢这个参考:
template <int N> // pass by value, clang is happy
void use_size(Foo<N> foo) { constexpr int n = size(foo); }
这里是live example
或者,您也可以复制您的foo对象并使用该本地对象:
template <int N>
void use_size(const Foo<N>& foo) {
auto f = foo;
constexpr int n = size(f);
}