use*_*522 10 c++ language-lawyer c++20 consteval
struct A {
~A() {}
consteval A() {}
consteval auto f() {}
};
int main() {
A{};
//A{}.f(); //1
}
Run Code Online (Sandbox Code Playgroud)
https://godbolt.org/z/4KPY5P7o7
该程序被 ICC、GCC 和 Clang 接受,但被 MSVC 拒绝,抱怨析构函数不在constexpr
立即函数调用中。
添加标记行//1
会导致所有四个编译器拒绝该代码。
问题:无论哪种情况,编译器都正确吗?如果正确,为什么?
请注意,这里有趣的部分是,A
由于非constexpr
平凡的析构函数,它是非字面的。删除其声明后,所有编译器都接受带有和不带有 的变体//1
。
constexpr
对于/函数和常量表达式,有一些特定于非文字类型的限制consteval
,但我认为它们中的任何一个都不应该适用于此。这些限制包括返回类型、参数类型、局部变量定义的类型、右值到左值的转换以及对象的修改。我想这里只有最后一个可以申请。但是[expr.const]/5.16中的修改到底意味着什么以及此处将修改哪个对象?
我还认为 MSVC 的抱怨是不正确的,因为对象的销毁不应该成为其构造函数立即调用的一部分。
另请参阅我之前启发这一问题的问题:constexpr 函数中的 Consteval 构造函数和成员函数调用
更新了更准确的标准参考:
我发现相关的部分(链接来自此处找到的 N4868 草案):
考虑以下示例:
struct A {
~A() {} // not constexpr
consteval int f() { return 1; }
};
template<class T>
consteval int f(T&& a) { return sizeof(a); }
consteval int f(int x) { return x; }
void g() {}
int main() {
A a;
f(a); // ok
a.f(); // ok
f(a.f()); // ok
f(sizeof(A{})); // ok
f(A{}); // not ok TYPE 1 (msvc) or TYPE 2 (clang, gcc)
A{}.f(); // not ok TYPE 1 (msvc) or TYPE 2 (clang, gcc)
f((A{},2)); // not ok TYPE 1 (clang, msvc) or TYPE 2 (gcc)
f((g(),2)); // not ok TYPE 1 (clang, gcc, icc, msvc)
}
Run Code Online (Sandbox Code Playgroud)
错误诊断是关于违反立即调用应为常量表达式的规定。
// msvc:
error C7595: 'f' ((or 'A::f')): call to immediate function is not a constant expression
// icc:
call to consteval function "f(T&&) [with T=A]" ((or "A::f" or "f(int)")) did not produce a valid constant expression
// clang:
error: call to consteval function 'f<A>' ((or 'A::f' or 'f')) is not a constant expression
Run Code Online (Sandbox Code Playgroud)
请注意,gcc 没有明确提及违反此 consteval/immediate 函数特定规则。
对于临时变量,我们从不同的编译器收到两种类型的诊断信息。有些人发现在常量(完整)表达式中调用非 constexpr 析构函数或函数存在问题。类型 1:
// msvc:
note: failure was caused by call of undefined function or one not declared 'constexpr'
note: see usage of 'A::~A' ((or 'g'))
// icc:
note: cannot call non-constexpr function "g"
// gcc:
error: call to non-'constexpr' function 'void g()'
// clang:
note: non-constexpr function '~A' ((or 'g')) cannot be used in a constant expression
Run Code Online (Sandbox Code Playgroud)
其他人(除了 icc,对此保持沉默)强调非文字类型临时变量不能出现在常量表达式中。类型2:
// gcc:
error: temporary of non-literal type 'A' in a constant expression
note: 'A' is not literal because:
note: 'A' does not have 'constexpr' destructor
// clang:
note: non-literal type 'A' cannot be used in a constant expression
Run Code Online (Sandbox Code Playgroud)
我认为出于保守考虑,由于 的隐式对象参数,这种情况A{}.f()
等同于这种情况。f(A{})
A::f
Fedor的令人惊讶的观察结果是,即使实现了调用eg,icc编译也是正确的。这A{A{}}.f()
A::A(const A&)
printf
code
,但没有输出任何内容。我认为这是一个错误。有趣的是,icc 会针对语义上非常相似的变体生成错误f(A{A{}})
。
我的原始帖子供参考(有助于理解一些评论):
对我来说,输出诊断很有意义。我关于立即调用的心理模型是这样的:您只能在立即上下文中使用立即函数。包含 constexpr 运算以外的任何内容的表达式不是直接上下文。
在您的示例中,表达式不仅是 constexpr 构造函数的调用,而且因为临时是表达式的一部分,所以它的销毁也应该作为表达式求值的一部分发生。因此,你的表达不再是直接的上下文。
我只是用placement new 调用构造函数来避免dtor 调用成为表达式的一部分,但placement new 本身也不被视为constexpr。我认为,从概念上讲,指针最好的解释是根本不应该出现在直接上下文中。
如果从表达式中删除ctor/dtor:
A a;
a.f();
Run Code Online (Sandbox Code Playgroud)
然后它编译得很好。
A{}.f()
ICC 中的一个有趣的错误是,即使使用dtor,它也无法编译constexpr
,并且无论你的定义多么微不足道,你都无法说服它f
:
error: call to consteval function "A::f" did not produce a valid constant expression
A{}.f();
^
Run Code Online (Sandbox Code Playgroud)
虽然它编译了上面列出的简单a.f()
变体,但没有任何抱怨。
归档时间: |
|
查看次数: |
908 次 |
最近记录: |