非 constexpr 调用的 constexpr 结果
最近,我很惊讶以下代码也在 clang、gcc 和 msvc 中编译(至少在它们当前的版本中)。
struct A {
static const int value = 42;
};
constexpr int f(A a) { return a.value; }
void g() {
A a; // Intentionally non-constexpr.
constexpr int kInt = f(a);
}
我的理解是,对 f
的调用不是 constexpr,因为参数 i
不是,但看来我错了。这是适当的标准支持的代码还是某种编译器扩展?
Recently I was surprised that the following code compiles in clang, gcc and msvc too (at least with their current versions).
struct A {
static const int value = 42;
};
constexpr int f(A a) { return a.value; }
void g() {
A a; // Intentionally non-constexpr.
constexpr int kInt = f(a);
}
My understanding was that the call to f
is not constexpr because the argument i
isn't, but it seems I am wrong. Is this a proper standard-supported code or some kind of compiler extension?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
正如注释中提到的,常量表达式的规则通常不要求表达式中提到的每个变量以及其生命周期开始于表达式求值之外的每个变量都是
constexpr
。有一个(长)要求列表,如果不满足这些要求,则会阻止表达式是一个常量表达式。只要不违反任何一个,该表达式就是常量表达式。
使用的变量/对象是
constexpr
的要求正式称为该对象可在常量表达式中使用(尽管确切的定义包含更详细的要求和例外情况,另请参阅链接cpp 参考页)。查看列表,您可以看到仅在某些情况下才需要此属性,即仅对于其生命周期在表达式之外开始的变量/对象,并且如果对其执行虚拟函数调用,则会执行左值到右值的转换或者它是表达式中命名的引用变量。
这两种情况都不适用于此。不涉及虚函数,并且
a
不是引用变量。通常,左值到右值的转换会导致需求变得重要。每当您尝试使用存储在对象或其子对象之一中的值时,就会发生左值到右值的转换。然而,A
是一个没有任何状态的空类,因此没有任何值可供读取。当将a
传递给函数时,会调用隐式复制构造函数来构造f
的参数,但由于类是空的,所以它实际上没有做任何事情。它不会访问a
的任何状态。请注意,如上所述,如果使用引用,则规则会更严格,例如
会失败,因为
ar
命名了一个在常量表达式中不可用的引用变量。希望这个问题能够尽快得到解决,以更加一致。 (请参阅https://github.com/cplusplus/papers/issues/973)As mentioned in the comments, the rules for constant expressions do not generally require that every variable mentioned in the expression and whose lifetime began outside the expression evaluation is
constexpr
.There is a (long) list of requirements that when not satisfied prevent an expression from being a constant expression. As long as none of them is violated, the expression is a constant expression.
The requirement that a used variable/object be
constexpr
is formally known as the object being usable in constant expressions (although the exact definition contains more detailed requirements and exceptions, see also linked cppreference page).Looking at the list you can see that this property is required only in certain situations, namely only for variables/objects whose lifetime began outside the expression and if either a virtual function call is performed on it, a lvalue-to-rvalue conversion is performed on it or it is a reference variable named in the expression.
Neither of these cases apply here. There are no virtual functions involved and
a
is not a reference variable. Typically the lvalue-to-rvalue conversion causes the requirement to become important. An lvalue-to-rvalue conversions happens whenever you try to use the value stored in the object or one of its subobjects. HoweverA
is an empty class without any state and therefore there is no value to read. When passinga
to the function, the implicit copy constructor is called to construct the parameter off
, but because the class is empty, it doesn't actually do anything. It doesn't access any state ofa
.Note that, as mentioned above, the rules are stricter if you use references, e.g.
will fail, because
ar
names a reference variable which is not usable in constant expressions. This will hopefully be fixed soon to be more consistent. (see https://github.com/cplusplus/papers/issues/973)