ash*_*hen 13 c++ c++11 list-initialization
struct A {
A(int) {}
};
struct B {
B(A) {}
};
int main() {
B b({0});
}
Run Code Online (Sandbox Code Playgroud)
构造出现b
以下错误:
In function 'int main()':
24:9: error: call of overloaded 'B(<brace-enclosed initializer list>)' is ambiguous
24:9: note: candidates are:
11:2: note: B::B(A)
10:8: note: constexpr B::B(const B&)
10:8: note: constexpr B::B(B&&)
Run Code Online (Sandbox Code Playgroud)
我期待B::B(A)
被召唤,为什么在这种情况下它是模棱两可的?
给定一个类,A
带有用户定义的构造函数:
struct A\n{\n A(int) {}\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n另一个,,B
接受A
作为构造函数参数:
struct B\n{\n B(A) {}\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n然后为了执行初始化,如下所示:
\n\nB b({0});\n
Run Code Online (Sandbox Code Playgroud)\n\n编译器必须考虑以下候选者:
\n\nB(A); // #1\nB(const B&); // #2\nB(B&&); // #3\n
Run Code Online (Sandbox Code Playgroud)\n\n试图从中找到隐式转换序列{0}
每个参数的隐式转换序列。
请注意,B b({0})
不进行列表初始化b
——(复制)列表初始化适用于构造函数参数本身。
由于参数是初始化列表,因此将参数与形参匹配所需的隐式转换序列是根据列表初始化序列[over.ics.list]/p1定义的定义的:
\n\n\n\n\n当参数是初始值设定项列表 ([dcl.init.list]) 时,它不是表达式,并且应用特殊规则将其转换为参数类型。
\n
上面写着:
\n\n\n\n\n[...],如果参数是非聚合类 X 并且根据 13.3.1.7 的重载决策选择 X 的单个\n 最佳构造函数来执行参数初始值设定项列表中类型 X 的对象的初始化,\n隐式转换序列是用户定义的转换序列,第二个标准转换序列是恒等转换。如果多个构造函数可行,但没有一个比其他构造函数更好,则隐式转换序列是不明确的转换序列。允许使用用户定义的转换来将初始化器列表元素转换为构造函数参数类型,除非 13.3.3.1 中另有说明。
\n
为了使 #1 可行,以下调用必须有效:
\n\nA a = {0};\n
Run Code Online (Sandbox Code Playgroud)\n\n这是正确的,因为[over.match.list]/p1这是正确的:
\n\n\n\n\n\xe2\x80\x94 如果没有找到可行的初始化列表构造函数,则再次执行重载决策,其中候选函数是该类的所有构造函数
\nT
,参数列表由初始化列表的元素组成。
即,类A
有一个接受一个构造函数int
参数的构造函数。
要使 #2 成为有效候选者,以下调用必须有效:
\n\nconst B& b = {0};\n
Run Code Online (Sandbox Code Playgroud)\n\n\n\n\n\n\n当引用类型的参数没有直接绑定到参数表达式时,转换序列就是根据[over.best.ics]将参数表达式转换为引用类型所需的序列。从概念上讲,此转换序列对应于使用参数表达式复制初始化引用类型的临时值。顶级简历资格中的任何差异都包含在初始化本身中,并且不构成转换。
\n
翻译为:
\n\nB b = {0};\n
Run Code Online (Sandbox Code Playgroud)\n\n再次关注[over.ics.list]/p6:
\n\n\n\n\n允许用户定义的转换将初始化列表元素转换为构造函数参数类型 [...]
\n
允许编译器使用用户定义的转换:
\n\nA(int);\n
Run Code Online (Sandbox Code Playgroud)\n\n将参数转换0
为B
构造函数参数A
。
对于候选#3,同样的推理适用于#2。最终,编译器无法在上述隐式转换序列{引用需要}之间进行选择,并报告歧义。
\n