G++和Clang:对于constexpr和const的行为不一致

5

请看以下代码:

constexpr const int A = 42;

const int &B = A;
static_assert(&A == &B, "Bug");

constexpr const int &C = B;
static_assert(&A == &C, "Bug");

int main() { return 0; }

在clang版本3.3中,它是完全被接受的,而g++ (SUSE Linux) 4.8.1 20130909 [gcc-4_8-branch revision 202388则拒绝了该方法,错误提示如下:

bug2.cpp:5:1: error: non-constant condition for static assertion
 static_assert(&A == &B, "Bug");
 ^
bug2.cpp:5:1: error: the value of ‘B’ is not usable in a constant expression
bug2.cpp:2:12: note: ‘B’ was not declared ‘constexpr’
 const int &B = A;
            ^

在我的看法中,GCC是正确的(尽管我更喜欢clang的行为)。尝试阅读标准后,我意识到自己不够成熟,无法做出决定。有人能确认吗?


1
clang 3.5在此代码上生成了一个错误,链接 - Shafik Yaghmour
1
Clang 项目发展非常迅速,3.3 版本已经过时。 - user703016
你真的想在某个地方“使用”这样的代码吗?只是好奇。 - n. m.
当然不是这样的。如果您想了解一些背景,请参阅我的回答https://dev59.com/C33aa4cB1Zd3GeqPaDYe - hivert
1
FYI,这可能与C++11和C++14之间的constexpr表达式规则变更有关;C++14引入了放宽的constexpr表达式规则,允许在constexpr函数中改变状态,因此C++14中的constexpr不意味着const(尽管名称很奇怪)。 - Matthieu M.
显示剩余3条评论
2个回答

3
“B”的值在常量表达式中不可用是错误的。您没有对B执行lvalue-to-rvalue转换,这是“value”的通常含义; 您只取其地址。唯一相关的常量表达式规则禁止:
引用类型的变量或数据成员的id-expression,除非引用具有前置初始化并使用常量表达式进行初始化;
然而,“B”确实有一个引用常量表达式的前置初始化。
参考文献:
引用常量表达式是指指定具有静态存储期或函数的对象的lvalue核心常量表达式。
在这种情况下,“B”的初始化程序是“A”,这是您可以获得的最常数值。A和B静态地引用同一对象。
因此,这是GCC的一个错误。

1

一般情况下,您不能将引用用作常量表达式,只能使用值(如果有例外,请纠正我)。在这个意义上,与您的代码最接近的有效方法是:

constexpr int A = 42;
constexpr int B = A;

static_assert(A == B, "Bug");

然而在我的情况下,无论是Clang 3.3还是GCC 4.8.1都会像预期的那样在您的代码上发生错误。
编辑:显然,我的知识仍然有些狭窄,抱歉。根据链接的不同,可能存在例外情况,例如如果语句是全局的,在main()之外,则会接受该代码(live)。

2
我认为你是错的。只需在行const int &B = A;中添加constexpr,我的代码似乎就正确了。 - hivert
@hivert 请看我的修改,当语句是全局的时候,代码可以编译通过,这一点在你的问题中没有明确说明。 - iavr
@iavr 是的,那会使它们变成静态的,这也是我在问题中评论的。 - Shafik Yaghmour
@hivert 这并不意味着您不能在常量表达式中使用它们:&a == &b,其中ab是本地对象,是核心常量表达式。 - jrok
2
@hivert 直观地讲,因为两个不同对象的地址不能相等,并且我们在编译时就知道了。但是请参见这里以获取标准化的讨论。 - jrok
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接