str*_*ger 16 c++ language-lawyer c++17
在std::initializer_list<U>
显式构造时,可以U
推导出模板参数()(例如,使用类模板参数推导(CTAD))吗?
换句话说,我知道以下语句是有效的:
std::initializer_list<int> x1{1, 2, 3};
std::initializer_list<int> x2 = {1, 2, 3};
auto x3 = std::initializer_list<int>{1, 2, 3};
Run Code Online (Sandbox Code Playgroud)
但是以下陈述也有效吗?
std::initializer_list x1{1, 2, 3};
std::initializer_list x2 = {1, 2, 3};
auto x3 = std::initializer_list{1, 2, 3};
Run Code Online (Sandbox Code Playgroud)
编译器不同意是否std::initializer_list
可以推导的模板参数:
#include <initializer_list>
struct s {
s(std::initializer_list<int>);
};
void f() {
std::initializer_list x1{1, 2, 3}; // Clang ERROR; GCC OK; MSVC OK
std::initializer_list x2 = {1, 2, 3}; // Clang ERROR; GCC OK; MSVC OK
auto x3 = std::initializer_list{1, 2, 3}; // Clang ERROR; GCC OK; MSVC OK
s x4(std::initializer_list{1, 2, 3}); // Clang ERROR; GCC ERROR; MSVC OK
s x5{std::initializer_list{1, 2, 3}}; // Clang ERROR; GCC OK; MSVC OK
s x6 = s(std::initializer_list{1, 2, 3}); // Clang ERROR; GCC OK; MSVC OK
s x7 = s{std::initializer_list{1, 2, 3}}; // Clang ERROR; GCC OK; MSVC OK
s x8 = std::initializer_list{1, 2, 3}; // Clang ERROR; GCC OK; MSVC OK
void g(std::initializer_list<int>);
g(std::initializer_list{1, 2, 3}); // Clang ERROR; GCC OK; MSVC OK
}
Run Code Online (Sandbox Code Playgroud)
(请参阅Compiler Explorer上的示例。)
编译器经过测试:
-std=c++17 -stdlib=libc++
和的Clang版本7.0.0-std=c++17 -stdlib=libstdc++
-std=c++17
/std:c++17
Clang是唯一正确的编译器。对真的。
当编译器看到没有模板参数的模板名称时,它必须查看模板的推导指南,并将其应用于braced-init-list中的参数。initializer_list
没有任何明确的推论指南,因此它使用了可用的构造函数。
拥有的唯一可公开访问的构造函数initializer_list
是其复制/移动构造函数及其默认构造函数。std::initializer_list
从括号初始列表创建不能通过可公开访问的构造函数来完成。这是通过列表初始化完成的,列表初始化是仅编译器的过程。只有编译器才能执行构建一个编译器所需的步骤。
考虑到所有这些,initializer_list
除非您要从现有列表中复制,否则应该不可能在上使用CTAD 。最后一部分可能是其他编译器在某些情况下如何使其工作。在推论方面,他们可以推导括号初始列表initializer_list<T>
本身,而不是作为要应用[over.match.list]的参数序列,因此推导指南会看到复制操作。