Adr*_*thy 8 c++ language-lawyer constexpr stdinitializerlist c++20
根据 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 deduced
// gcc: not constant expression
// clang: no viable c'tor or deduction guide
// msvc: works
constexpr auto the_list = std::initializer_list{1, 2, 3};
// 3. Static assert using constexpr size
// gcc: fails because of above
// clang: fails because of above
// msvc: works
static_assert(the_list.size() == 3);
// 4. Extract the size via a constexpr function
// gcc: fails because of above
// clang: fails because of above
// msvc: expression did not evaluate to a constant
constexpr auto the_list_size = Foo(the_list);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
-std=c++20(也使用 13.1 进行测试)-std=c++20(也使用 16.0.0 进行测试)/std:c++20我本来希望所有四个案例都能编译。但有些编译器拒绝了其中的一些,导致我重新考虑我的理解。编译器是否拒绝正确的代码(或接受错误的代码)?我是否无意中依赖了实现定义的行为?标准是否含糊?
我能找到的最接近的现有问题是分配一个initializer_list to std::array,但其中的细节是特定于std::array 的,这在我的示例中不是这种情况。
std::initializer_list{1, 2, 3}无效是一个 clang bug(它无法<int>正确推断。用 替换它会std::initializer_list<int>{1, 2, 3}导致与 gcc 相同的行为。或者你可以用 绕过它auto the_list = { 1, 2, 3 };。
的问题与constexpr auto the_list = std::initializer_list<int>{1, 2, 3};的问题相同constexpr const int& the_number = 123;。与 with 类似const int&,std::initializer_list绑定到临时对象(在本例中为数组)。同样const int&,临时变量的生命周期也延长到initializer_list变量的生命周期。
如果constexpr变量绑定到临时变量,则该临时变量需要具有静态存储持续时间。所以,static constexpr auto the_list = std::initializer_list<int>{1, 2, 3};有效(也是如此static constexpr const int& the_number = 123;)。
\n\n\n类型的对象
\nstd\xe2\x80\x8b::\xe2\x80\x8binitializer_\xc2\xadlist<E>是从初始值设定项列表构造的,就好像实现生成并具体化 ([conv.rval]) Nconst E\xe2\x80\ x9d、[...] 的 \xe2\x80\x9carray 类型的纯右值,并且该std\xe2\x80\x8b::\xe2\x80\x8binitializer_\xc2\xadlist<E>对象被构造为引用该数组。[...]
\n\n该数组与任何其他临时对象 ([class.temporary]) 具有相同的生命周期,只不过
\ninitializer_\xc2\xadlist从数组初始化对象会延长数组的生命周期,就像将引用绑定到临时对象一样。
(例如auto the_list = { 1, 2, 3 };,const int[3]具有自动存储持续时间,但是static auto the_list = { 1, 2, 3 };,const int[3]具有静态存储持续时间)
\n\n如果实体是具有静态存储持续时间的对象,并且不是临时对象或者是其值满足上述约束的临时对象,则实体是常量表达式的允许结果,[...]
\n
你的最后一个问题不是Foo函数constexpr。固定版本:
#include <initializer_list>\n\nusing size_type = std::initializer_list<int>::size_type;\n\ntemplate <typename T>\nconstexpr size_type Foo(std::initializer_list<T> const &list) {\n return list.size();\n}\n\nint main() {\n static_assert(std::initializer_list<int>{1, 2, 3}.size() == 3);\n\n static constexpr auto the_list = {1, 2, 3};\n\n static_assert(the_list.size() == 3);\n\n constexpr auto the_list_size = Foo(the_list);\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n在三个给定的编译器上编译。之前MSVC对于临时数组的生命周期太宽松了。
\n| 归档时间: |
|
| 查看次数: |
179 次 |
| 最近记录: |