constexpr 表达式和变量生存期,G++ 和 Clang 不同意的一个例子

constexpr expression and variable lifetime, an example where g++ and clang disagree

本文关键字:一个 不同意 变量 表达式 生存期 Clang G++ constexpr      更新时间:2023-10-16

考虑简单的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.

(注意:编译器版本。我已经使用 g++ 版本 5.3.1 和 7.2.1 以及 clang++ 版本 3.6.2 和 5.0.0 检查了前面的语句(

我的问题:g++ 或 clang 哪个是对的?问题出在哪里?

我的解释是 clang++ 是对的,而 g++ 太宽松了。

我们可以在标准 https://isocpp.org/std/the-standard 中找到一个接近的例子([expr.const]部分,第126页((草稿可以下载,注意大PDF!

constexpr int g(int k) { 
    constexpr int x = incr(k); 
    return x; 
}

其中解释为:

错误:incr(k( 不是核心常量表达式,因为 K的寿命始于表达式incr(k(之外

这正是使用 foo 参数的 use_size() 函数中发生的情况,即使 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); }

我预计Clang在这种情况下是错误的。它应该将您的函数调用评估为常量表达式,这仅仅是因为您只使用模板参数,而不是对象本身。由于您不在 constexpr 函数中使用该对象,因此应该不会禁止编译时计算。

但是,标准中有一条规则,即在常量表达式(如引用(之前开始其生命周期的对象不能用作constexpr。

在这种情况下,有一个简单的修复程序。我认为它不喜欢参考:

template <int N> // pass by value, clang is happy
void use_size(Foo<N> foo) { constexpr int n = size(foo); }

这是一个活生生的例子

或者,您也可以复制 foo 对象并使用该本地对象:

template <int N>
void use_size(const Foo<N>& foo) {
    auto f = foo;
    constexpr int n = size(f);
}

现场示例

相关文章: