通过引用调用`constexpr`成员函数-clang vs gcc
Invoking `constexpr` member function through reference - clang vs gcc
考虑以下示例(代码段 (0)):
struct X
{
constexpr int get() const { return 0; }
};
void foo(const X& x)
{
constexpr int i = x.get();
}
int main()
{
foo(X{});
}
以上示例使用 g++ 10.x
之前的所有 g++
版本进行编译,并且从未在 clang++
下编译。错误信息是:
error: 'x' is not a constant expression
8 | constexpr int i = x.get();
|
这种错误是有道理的,因为 x
从来都不是 foo
正文中的常量表达式,但是:
X::get()
被标记为constexpr
并且不依赖于x
的状态;
将 const X&
更改为 const X
使代码可以通过每个编译器进行编译 (on godbolt.org) snippet (1).
当我将 X::get()
标记为 static
((on godbolt.org) 片段 (2) 时,它变得更加有趣。通过该更改,所有测试版本的 g++
(包括主干)都可以编译,而 clang++
仍然总是无法编译。
所以,我的问题:
g++ 9.x
接受片段(0)是否正确?
是否所有编译器都正确接受代码段 (1)?如果是这样,为什么引用很重要?
g++ 9.x
和 g++ trunk
接受 片段 (2) 是否正确?
Is g++ 9.x correct in accepting snippet (0)?
没有
Are all compilers correct in accepting snippet (1)? If so, why is the reference significant?
是的,他们是。
常量表达式不能使用 id-expression 命名没有先前常量表达式初始化或在常量表达式评估期间开始其生命周期的引用。 [expr.const]/2.11 (same in C++20)
如果您在不涉及任何左值到右值转换的情况下命名非引用变量,则情况并非如此。 x.get()
仅将 x
引用为左值,并且仅调用实际上不访问 x
的任何成员的 constexpr
函数,因此没有问题。
Are g++ 9.x and g++ trunk correct in accepting snippet (2)?
否,因为表达式中仍然包含违反上述规则的子表达式x
。
考虑以下示例(代码段 (0)):
struct X
{
constexpr int get() const { return 0; }
};
void foo(const X& x)
{
constexpr int i = x.get();
}
int main()
{
foo(X{});
}
以上示例使用 g++ 10.x
之前的所有 g++
版本进行编译,并且从未在 clang++
下编译。错误信息是:
error: 'x' is not a constant expression 8 | constexpr int i = x.get(); |
这种错误是有道理的,因为 x
从来都不是 foo
正文中的常量表达式,但是:
X::get()
被标记为constexpr
并且不依赖于x
的状态;将
const X&
更改为const X
使代码可以通过每个编译器进行编译 (on godbolt.org) snippet (1).
当我将 X::get()
标记为 static
((on godbolt.org) 片段 (2) 时,它变得更加有趣。通过该更改,所有测试版本的 g++
(包括主干)都可以编译,而 clang++
仍然总是无法编译。
所以,我的问题:
g++ 9.x
接受片段(0)是否正确?是否所有编译器都正确接受代码段 (1)?如果是这样,为什么引用很重要?
g++ 9.x
和g++ trunk
接受 片段 (2) 是否正确?
Is g++ 9.x correct in accepting snippet (0)?
没有
Are all compilers correct in accepting snippet (1)? If so, why is the reference significant?
是的,他们是。
常量表达式不能使用 id-expression 命名没有先前常量表达式初始化或在常量表达式评估期间开始其生命周期的引用。 [expr.const]/2.11 (same in C++20)
如果您在不涉及任何左值到右值转换的情况下命名非引用变量,则情况并非如此。 x.get()
仅将 x
引用为左值,并且仅调用实际上不访问 x
的任何成员的 constexpr
函数,因此没有问题。
Are g++ 9.x and g++ trunk correct in accepting snippet (2)?
否,因为表达式中仍然包含违反上述规则的子表达式x
。