fre*_*low 35 c++ initializer-list c++11
出于某种原因,我认为C++ 0x允许std::initializer_list作为函数的函数参数,例如,期望可以从这样构造的类型std::vector.但显然,它不起作用.这只是我的编译器,还是永远不会起作用?是因为潜在的重载解决问题吗?
#include <string>
#include <vector>
void function(std::vector<std::string> vec)
{
}
int main()
{
// ok
std::vector<std::string> vec {"hello", "world", "test"};
// error: could not convert '{"hello", "world", "test"}' to 'std::vector...'
function( {"hello", "world", "test"} );
}
Run Code Online (Sandbox Code Playgroud)
Joh*_*itb 55
GCC有一个错误.标准使这个有效.看到:
请注意,这有两个方面
第一个问题在部分回答8.5.第二个问题在部分回答13.3.例如,引用绑定在8.5.3和处理13.3.3.1.4,而列表初始化在8.5.4和中处理13.3.3.1.5.
8.5/14,16:
在表单中发生的初始化
Run Code Online (Sandbox Code Playgroud)T x = a;以及参数传递,函数返回,抛出异常(15.1),处理异常(15.3)和聚合成员初始化(8.5.1)称为复制初始化.
.
.
初始化器的语义如下[...]:如果初始化器是braced-init-list,则对象是列表初始化的(8.5.4).
在考虑候选者时function,编译器将看到一个初始化列表(它还没有类型 - 它只是一个语法结构!)作为参数,而a std::vector<std::string>作为参数function.为了弄清楚转换的成本是什么,以及我们是否可以在超载的情况下转换它们,13.3.3.1/5说
13.3.3.1.5/1:
当参数是初始化列表(8.5.4)时,它不是表达式,并且特殊规则适用于将其转换为参数类型.
13.3.3.1.5/3:
否则,如果参数是非聚合类X并且每个13.3.1.7的重载决策选择X的单个最佳构造函数来从参数初始化列表中执行类型X的对象的初始化,则隐式转换序列是用户 - 定义转换序列.除了13.3.3.1中所述之外,允许用户定义的转换将初始化列表元素转换为构造函数参数类型.
非聚合类X是std::vector<std::string>,我将找出下面的单个最佳构造函数.最后一条规则允许我们在以下情况下使用用户定义的转换:
struct A { A(std::string); A(A const&); };
void f(A);
int main() { f({"hello"}); }
Run Code Online (Sandbox Code Playgroud)
我们被允许将字符串文字转换为std::string,即使这需要用户定义的转换.但是,它指出了另一段的限制.怎么13.3.3.1说?
13.3.3.1/4,这是负责禁止多个用户定义的转换的段落.我们只会查看列表初始化:
但是,在考虑用户定义的转换函数[(或构造函数)]的参数时,当将初始化列表作为单个参数传递或初始化列表只有一个元素时,它是[...] 13.3.1.7的候选者并且转换为某个类X或引用(可能是cv-quali fi ed)X被认为是X的构造函数的第一个参数,或者[...],只允许标准转换序列和省略号转换序列.
请注意,这是一个重要的限制:如果不是这样,上面的内容可以使用copy-constructor建立一个同样好的转换序列,初始化将是不明确的.(注意该规则中"A或B和C"的潜在混淆:它意味着说"(A或B)和C" - 因此我们仅在尝试通过具有参数的X的构造函数进行转换时受到限制.类型X).
我们被委托13.3.1.7收集可用于进行此转换的构造函数.让我们从一般方面接近本段,从8.5授权我们开始8.5.4:
8.5.4/1:
列表初始化可以在直接初始化或复制初始化上下文中进行; 直接初始化上下文中的列表初始化称为直接列表初始化,复制初始化上下文中的列表初始化称为复制列表初始化.
8.5.4/2:
构造函数是初始化列表构造函数,如果它的第一个参数类型
std::initializer_list<E>或引用可能std::initializer_list<E>是某些类型E的cv-quali ,并且没有其他参数或者所有其他参数都有默认参数(8.3.6).
8.5.4/3:
类型T的对象或引用的列表初始化定义如下:[...]否则,如果T是类类型,则考虑构造函数.如果T有一个初始化列表构造函数,则参数列表由初始化列表作为单个参数组成; 否则,参数列表由初始化列表的元素组成.列举了适用的构造函数(13.3.1.7),并通过重载解析(13.3)选择最佳构造函数.
这时,T是班级类型std::vector<std::string>.我们有一个参数(它还没有类型!我们只是在具有语法初始化列表的上下文中).构造函数列举如下13.3.1.7:
[...]如果T有一个初始化列表构造函数(8.5.4),则参数列表由初始化列表作为单个参数组成; 否则,参数列表由初始化列表的元素组成.对于复制列表初始化,候选函数是T的所有构造函数.但是,如果选择了显式构造函数,则初始化是错误的.
我们只会将初始化列表std::vector视为唯一的候选者,因为我们已经知道其他人不会赢得反对或者不符合论证.它有以下签名:
vector(initializer_list<std::string>, const Allocator& = Allocator());
Run Code Online (Sandbox Code Playgroud)
现在,将初始化列表转换为std::initializer_list<T>(以对参数/参数转换的成本进行分类)的规则列举在13.3.3.1.5:
当参数是初始化列表(8.5.4)时,它不是表达式,并且特殊规则适用于将其转换为参数类型.[...]如果参数类型是
std::initializer_list<X>并且初始化列表的所有元素都可以隐式转换为X,则隐式转换序列是将列表元素转换为X所需的最差转换.此转换可以是用户-甚至在调用初始化列表构造函数的上下文中也定义了转换.
现在,初始化列表将成功转换,转换序列是用户定义的转换(从char const[N]到std::string).如何做到这一点8.5.4再详细说明:
否则,如果T是特化
std::initializer_list<E>,则初始化器列表对象的构造如下所述,并用于根据相同类型的类初始化对象的规则来初始化对象(8.5).(......)
看看8.5.4/4这最后一步是怎么做的:)