非 constexpr 调用的 constexpr 结果

发布于 2025-01-16 02:32:39 字数 393 浏览 4 评论 0原文

最近,我很惊讶以下代码也在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

守护在此方 2025-01-23 02:32:39

正如注释中提到的,常量表达式的规则通常不要求表达式中提到的每个变量以及其生命周期开始于表达式求值之外的每个变量都是 constexpr

有一个(长)要求列表,如果不满足这些要求,则会阻止表达式是一个常量表达式。只要不违反任何一个,该表达式就是常量表达式。

使用的变量/对象是 constexpr 的要求正式称为该对象可在常量表达式中使用(尽管确切的定义包含更详细的要求和例外情况,另请参阅链接cpp 参考页)。

查看列表,您可以看到仅在某些情况下才需要此属性,即仅对于其生命周期在表达式之外开始的变量/对象,并且如果对其执行虚拟函数调用,则会执行左值到右值的转换或者它是表达式中命名的引用变量。

这两种情况都不适用于此。不涉及虚函数,并且 a 不是引用变量。通常,左值到右值的转换会导致需求变得重要。每当您尝试使用存储在对象或其子对象之一中的值时,就会发生左值到右值的转换。然而,A 是一个没有任何状态的空类,因此没有任何值可供读取。当将a传递给函数时,会调用隐式复制构造函数来构造f的参数,但由于类是空的,所以它实际上没有做任何事情。它不会访问 a 的任何状态。

请注意,如上所述,如果使用引用,则规则会更严格,例如

A a;
A& ar = a;
constexpr int kInt = f(ar);

会失败,因为 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. However A is an empty class without any state and therefore there is no value to read. When passing a to the function, the implicit copy constructor is called to construct the parameter of f, but because the class is empty, it doesn't actually do anything. It doesn't access any state of a.

Note that, as mentioned above, the rules are stricter if you use references, e.g.

A a;
A& ar = a;
constexpr int kInt = f(ar);

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)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文