考虑以下示例(代码段 (0)):
struct X
{
constexpr int get() const { return 0; }
};
void foo(const X& x)
{
constexpr int i = x.get();
}
int main()
{
foo(X{});
}
Run Code Online (Sandbox Code Playgroud)
与所有版本上面的例子编译g++之前g++ 10.x,从来没有下编译clang++。错误信息是:
Run Code Online (Sandbox Code Playgroud)error: 'x' is not a constant expression 8 | constexpr int i = x.get(); |
错误是有道理的,因为x在 的主体中从来不是一个常量表达式foo,但是:
X::get()被标记constexpr并且它不依赖于的状态x;
更改const X&为const X使用每个编译器(在 Godbolt.org 上) 片段 …
[expr.unary.noexcept]的措辞在C++17 中发生了变化。
以前(n4140, 5.3.7 noexcept operator [expr.unary.noexcept]),我的重点是:
- 如果在潜在求值的上下文中表达式将包含,则 noexcept 运算符的结果为 false
(3.1) 对不具有非抛出异常规范 ([except.spec]) 的函数、成员函数、函数指针或成员函数指针的潜在求值调用,除非调用是常量表达式 ([ expr.const]) ...
现在1(7.6.2.6 noexcept 运算符 [expr.unary.noexcept]):
- 除非表达式可能抛出异常 ([except.spec]),否则noexcept 运算符的结果为真。
- 如果函数声明没有 noexcept 说明符,则该声明具有潜在的抛出异常说明,除非...
但是14.5(3)的除非列表未列出constexpr,因此可能会抛出...
1 LF 在评论中添加的指向 C++17 n4659的链接。
constexpr int f(int i) { return i; }
std::cout << boolalpha << noexcept(f(7)) << std::endl;
int a = 7;
std::cout …Run Code Online (Sandbox Code Playgroud) 从 C++20 开始,std::isnan 不是 constexpr。
这有技术原因吗?
我能想到的只是它在“C 头文件”中,所以没有人费心去修复它(<limits>处理浮点数的函数是 constexpr,但不幸的numeric_limits是不包含 isnan 检查)。
以下代码不能在 clang 中编译(在 GCC 中可以):
struct A{
int a;
};
auto test(){
constexpr A x{10};
return []{
return x; // <-- here x is A: clang doesn't compile
}();
}
Run Code Online (Sandbox Code Playgroud)
Clang 的错误是变量 'x' 不能在没有指定捕获默认值的 lambda 中隐式捕获,但我认为 constexpr 变量总是被捕获。
如果 x 是 int,则代码编译:
auto test(){
constexpr int x{10};
return []{
return x; // <-- here x is int: clang is ok
}();
}
Run Code Online (Sandbox Code Playgroud)
有趣的是,以下代码也可以编译:
auto test(){
constexpr A x{10};
return []{
return x.a;
}();
}
Run Code Online (Sandbox Code Playgroud)
叮当正确吗?如果是这样,理由是什么?我正在使用 -std=c++17
- 编辑 …
我的一位同事向我展示了这个令人震惊的 C++20 程序:
#include <iostream>
constexpr int p(auto) { return 0; }
constexpr int q() { return p(0); }
constexpr int p(auto) requires true { return 1; }
static_assert(p(0) == 1);
static_assert(q() == 0);
int main()
{
std::cout << q() << p(0) << '\n';
}
Run Code Online (Sandbox Code Playgroud)
由于错误,GCC 无法构建它:
Error: symbol `_Z1pIiEiT_' is already defined
Run Code Online (Sandbox Code Playgroud)
Clang 成功构建程序并打印11(https://gcc.godbolt.org/z/1Gf5vj5oo)。所以static_assert(q() == 0)被成功检查,但std::cout << q()仍然打印1. 怎么会这样?
Visual Studio 2019 16.10.4 的行为更加怪异。在 Release 配置中它也打印11,在 Debug …
最近,我很惊讶以下代码也在 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);
}
Run Code Online (Sandbox Code Playgroud)
我的理解是,调用f不是 constexpr 因为参数i不是,但看来我错了。这是适当的标准支持的代码还是某种编译器扩展?
关键字constexpr在引入 C++11 标准时对其函数实施了相当严格的限制。C++14 和 C++20 放宽了这些限制(最值得注意):
return语句static_assert等。try并且asmC++23 进一步软化了这些限制。从我在cppreference中看到的,constexprfor函数似乎只剩下以下含义:
C++23 甚至删除了 constexpr 函数必须在编译时对于p2448r2中的任何类型“可计算”的限制。根据我的理解,这完全消除了constexpr在编译时评估函数的想法。
是这样吗?如果是这样,constexpr函数还有什么用处呢?
可以在函数中使用static局部变量吗?constexpr例如:
#include <string_view>
#include <utility>
enum class axis {
x, y, z
};
constexpr std::string_view axis_name(axis a) {
// use static constexpr to avoid putting the table onto the stack
static constexpr std::string_view names[] {
"x", "y", "z"
};
return names[std::to_underlying(a)];
}
constexpr auto x_name = axis_name(axis::x);
Run Code Online (Sandbox Code Playgroud)
GCC 12 无法编译此错误:
<source>:9:39: error: 'names' defined 'static' in 'constexpr' context
9 | static constexpr std::string_view names[] {
| ^~~~~
Run Code Online (Sandbox Code Playgroud)
其他编译器允许它。规则是什么?什么时候允许?
static一般使用,或者static const,或者根据 cppreference.com,std::initializer_lists 具有constexpr 构造函数和constexpr 大小方法(C++14 起)。
尽管我使用的编译器似乎同意 constexpr 初始值设定项列表的大小确实是 constexpr,但在某些情况下,它不相信我的列表是 constexpr。由于 std::initializer_lists 可能涉及一些“编译器魔法”,我开始想知道 constexpr 是否不适用于它们,其方式与适用于非魔法对象的方式完全相同。
我跳到 Compiler Explorer 上,发现主要编译器在这个主题上并不一致。
那么以下四种情况的正确行为(根据标准)是什么?
#include <initializer_list>
using size_type = std::initializer_list<int>::size_type;
template <typename T>
size_type Foo(std::initializer_list<T> const &list) {
return list.size();
}
int main() {
// 1. Example based on
// https://en.cppreference.com/w/cpp/utility/initializer_list/size
// gcc: works
// clang: no viable c'tor or deduction guide
// msvc: works
static_assert(std::initializer_list{1, 2, 3}.size() == 3);
// 2. Make a constexpr std::initializer_list<T> with T …Run Code Online (Sandbox Code Playgroud) 在以下程序中,struct 的默认构造函数A不会初始化其字段v。然后在常量表达式中,std::vector<A>放置有A()对象:
#include <vector>
struct A {
constexpr A() noexcept {}
int v;
};
constexpr bool f() {
std::vector<A> as;
as.reserve(1);
as.emplace_back();
return true;
}
static_assert( f() );
Run Code Online (Sandbox Code Playgroud)
MSVC 编译器抱怨读取未初始化的变量:
<source>(14): error C2131: expression did not evaluate to a constant
<source>(11): note: failure was caused by a read of an uninitialized symbol
<source>(11): note: see usage of 'A::v'
Run Code Online (Sandbox Code Playgroud)
但 GCC 和 Clang 都对该程序很满意。在线演示: https: //godbolt.org/z/addx11aTT
这里哪个编译器是正确的?