struct A {
consteval A() {};
};
constexpr bool g() {
auto a = new A;
delete a;
return true;
}
int main() {
static_assert(g());
}
Run Code Online (Sandbox Code Playgroud)
https://godbolt.org/z/jsq35WxKs
GCC 和 MSVC 拒绝该程序,ICC 和 Clang 接受它:
///MSVC:
<source>(6): error C7595: 'A::A': call to immediate function is not a constant expression
Compiler returned: 2
//GCC:
<source>: In function 'constexpr bool g()':
<source>:6:18: error: the value of '<anonymous>' is not usable in a constant expression
6 | auto a = new A;
| ^ …
Run Code Online (Sandbox Code Playgroud) 显然,consteval
它将成为C++ 20中的关键字.它的cppreference页面目前是空白的.它会是什么以及它与它有什么关系constexpr
?
C ++中的常量表达式具有非常整洁的特性:它们的求值不能具有未定义的行为(7.7.4.7):
表达式e是核心常量表达式,除非按照抽象机([intro.execution])的规则对e求值,将求出以下值之一:
...
一种操作,其操作将具有本文档的[引言] [cpp]中指定的不确定的行为[注意:例如,包括带符号整数溢出([expr.prop]),某些指针算术([expr.add],除以零或某些移位操作-尾注];
尝试将13!
in 的值存储在constexpr int
确实产生一个不错的编译错误:
constexpr int f(int n)
{
int r = n--;
for (; n > 1; --n) r *= n;
return r;
}
int main()
{
constexpr int x = f(13);
return x;
}
Run Code Online (Sandbox Code Playgroud)
输出:
9:19: error: constexpr variable 'x' must be initialized by a constant expression
constexpr int x = f(13);
^ ~~~~~
4:26: note: value 3113510400 is outside the range of representable values …
Run Code Online (Sandbox Code Playgroud) 考虑最小化的代码片段:
#include <vector>
class Bar
{
public:
constexpr Bar() {}
};
consteval Bar foo()
{
return Bar();
}
int main()
{
std::vector<Bar> bars{ foo(), foo() };
}
Run Code Online (Sandbox Code Playgroud)
这不能在最新的 MSVC 编译器(Visual Studio 2022 版本 17.3.3)上编译,但可以在 Clang 或 GCC 上编译。
代码是否格式不正确,或者是 MSVC 中的错误?
在下面的代码中,struct A
有立即函数默认构造函数,并在动态内存中创建该结构的对象new A{}
:
struct A {
consteval A() {}
};
int main() {
new A{};
}
Run Code Online (Sandbox Code Playgroud)
只有 Clang 接受。
海湾合作委员会抱怨
error: the value of '<anonymous>' is not usable in a constant expression
6 | new A{};
| ^
note: '<anonymous>' was not declared 'constexpr'
Run Code Online (Sandbox Code Playgroud)
MSVC 也这样做:
error C7595: 'A::A': call to immediate function is not a constant expression
Run Code Online (Sandbox Code Playgroud)
演示: https: //gcc.godbolt.org/z/6Px5WYGzd
这里是哪个编译器?
在以下结构体定义中,构造函数A(int)
将其工作委托给立即函数构造函数A()
:
struct A {
int i = 0;
consteval A() = default;
A(int) : A() {}
};
Run Code Online (Sandbox Code Playgroud)
Clang 接受了它,但 GCC 没有抱怨:
error: 'this' is not a constant expression
Run Code Online (Sandbox Code Playgroud)
和 MSVC:
'A::A': call to immediate function is not a constant expression
Run Code Online (Sandbox Code Playgroud)
演示: https: //gcc.godbolt.org/z/7e3fWzYzr
哪个编译器是正确的?
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 构造函数和成员函数调用
从 C++20 开始,std::vector
可以在常量表达式中使用。据我所知,当前的 C++ 允许动态分配内存,条件是任何此类分配在常量表达式“结束”时都被释放。
但是,我遇到的情况是,在立即运行的情况下,规则可能会有所不同。请考虑以下示例:
consteval auto getVec() {
return std::vector<int>(9);
}
static_assert( getVec().size() == 9 );
Run Code Online (Sandbox Code Playgroud)
这里立即consteval
函数getVec
返回 not-empty std::vector
,其大小在常量表达式中验证。
我期望这段代码能够编译,因为所有释放都必须自动完成,并且实际上它在 Clang 中被接受libc++
。
但 MSVC 抱怨道:
error C7595: 'getVec': call to immediate function is not a constant expression
note: (sub-)object points to memory which was heap allocated during constant evaluation
fatal error C1903: unable to recover from previous error(s); stopping compilation
Run Code Online (Sandbox Code Playgroud)
GCC 的行为类似:
error: 'getVec()()' is not a constant expression …
Run Code Online (Sandbox Code Playgroud) 当前,即使对它的所有调用确实为,也无法用于static_assert
验证constexpr
函数的参数constexpr
。这是有道理的,因为在某些其他模块尝试调用该函数的情况下,编译器仍必须为此函数创建一个非constexpr实例。可悲的是,即使函数是static
或位于匿名名称空间中,也是如此。
然而,C ++ 20将引入一个新的关键字consteval
,类似于constexpr
但不允许以非constexpr的方式调用函数。在这种情况下,编译器可以确定在编译时始终知道函数参数。因此,从理论上讲,应该有可能使用进行验证static_assert
。
问题是:标准允许吗?
例:
#include <iostream>
consteval char operator""_bchar(const char text[], const size_t length)
{
static_assert(length == 8, "Binary char has to have 8 digits!"); // <-- This is currently not possible.
uint8_t byte = 0;
for (size_t i = 0; i != length; ++i)
{
byte <<= 1;
byte |= text[i] == '1' ? 0b00000001 : 0b00000000;
}
return byte;
}
int main() …
Run Code Online (Sandbox Code Playgroud) clang(trunk) 给出以下代码的错误:
consteval void f() {}
int main()
{
f(); // error: call to consteval function 'f' is not a constant expression
// note: subobject of type 'void' is not initialized
}
Run Code Online (Sandbox Code Playgroud)
而 gcc(trunk) 编译它没有错误。
我觉得这可能是一个 clang bug,因为 gcc 和 clang 都接受这个代码:
consteval int g() { return 42; }
int main()
{
g(); // ok
}
Run Code Online (Sandbox Code Playgroud)
这是要玩的代码。
那么这是一个 clang bug,还是代码格式不正确,或者有 ub,还是其他什么?
编辑:我觉得指出 clang 允许f
从其他函数调用(如果它们也是保守的)可能是相关的。f
它仅在从非 consteval 函数调用时给出错误:
consteval int h()
{
f(); // ok
return 42; …
Run Code Online (Sandbox Code Playgroud)