带有列表初始化的模糊构造函数调用
Ambigous constructor call with list-initialization
struct A {
A(int) {}
};
struct B {
B(A) {}
};
int main() {
B b({0});
}
构造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&&)
我原以为 B::B(A)
会被调用,为什么在这种情况下会模棱两可?
B b({0})
可以调用以下任一方法:
B::B(A)
B
的复制构造函数:从 {0}
构造临时 B
对象并
然后将其复制到 b
.
因此含糊不清。
调用B b{0}
即可解决,直接使用定义的构造函数,不涉及拷贝构造函数
编辑:
关于第2点如何有效:
B
有一个接受 A
的构造函数。现在,A
可以由 int
构造。此外, int
可以通过初始化列表构造。这就是为什么这是一个有效的案例。如果 A
的构造函数是 explicit
,从 {0}
到 int
的自动转换就会失败,从而不会产生歧义。
使用 GCC8 可以很好地编译代码。
这应该不是模棱两可的称呼。对于B
的copy/move构造函数被调用,那么对于B b({0});
需要以下步骤:
- 通过
A::A(int)
从0
构建A
- 从
A
由 B::B(A)
在步骤 1 中构建的 B
- 从
B
由 B
. 的 copy/move 构造函数在步骤 2 中构造 b
这意味着需要两个用户定义的转换(步骤#1 和#2),但这在一个隐式转换序列中是不允许的。
给定一个 class、A
和一个用户定义的构造函数:
struct A
{
A(int) {}
};
和另一个 B
,接受 A
作为构造函数参数:
struct B
{
B(A) {}
};
然后为了执行如下初始化:
B b({0});
编译器必须考虑以下候选者:
B(A); // #1
B(const B&); // #2
B(B&&); // #3
试图找到从 {0}
到每个参数的隐式转换序列。
请注意 B b({0})
不进行列表初始化 b
--(复制)列表初始化适用于构造函数参数本身。
由于参数是一个初始化列表,将参数与参数匹配所需的隐式转换序列是根据列表初始化序列定义的 [over.ics.list]/p1:
When an argument is an initializer list ([dcl.init.list]), it is not an expression and special rules apply for converting it to a parameter type.
上面写着:
[...], if the parameter is a non-aggregate class X and overload resolution per 13.3.1.7 chooses a single
best constructor of X to perform the initialization of an object of type X from the argument initializer list,
the implicit conversion sequence is a user-defined conversion sequence with the second standard conversion
sequence an identity conversion. If multiple constructors are viable but none is better than the others, the
implicit conversion sequence is the ambiguous conversion sequence. User-defined conversions are allowed
for conversion of the initializer list elements to the constructor parameter types except as noted in 13.3.3.1.
要使#1 可行,以下调用必须有效:
A a = {0};
这是正确的,因为 [over.match.list]/p1:
— If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T
and the argument list consists of the elements of the initializer list.
即,class A
有一个接受 int
参数的构造函数。
要使 #2 成为有效候选者,以下调用必须有效:
const B& b = {0};
When a parameter of reference type is not bound directly to an argument expression, the conversion sequence is the one required to convert the argument expression to the referenced type according to [over.best.ics]. Conceptually, this conversion sequence corresponds to copy-initializing a temporary of the referenced type with the argument expression. Any difference in top-level cv-qualification is subsumed by the initialization itself and does not constitute a conversion.
转换为:
B b = {0};
再次关注[over.ics.list]/p6:
User-defined conversions are allowed for conversion of the initializer list elements to the constructor parameter types [...]
允许编译器使用用户定义的转换:
A(int);
将参数 0
转换为 B
的构造函数参数 A
。
对于候选人#3,同样的推理适用于#2。最终,编译器无法在上述隐式转换序列之间进行选择 {citation needed},并报告歧义。
struct A {
A(int) {}
};
struct B {
B(A) {}
};
int main() {
B b({0});
}
构造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&&)
我原以为 B::B(A)
会被调用,为什么在这种情况下会模棱两可?
B b({0})
可以调用以下任一方法:
B::B(A)
B
的复制构造函数:从{0}
构造临时B
对象并 然后将其复制到b
.
因此含糊不清。
调用B b{0}
即可解决,直接使用定义的构造函数,不涉及拷贝构造函数
编辑:
关于第2点如何有效:
B
有一个接受 A
的构造函数。现在,A
可以由 int
构造。此外, int
可以通过初始化列表构造。这就是为什么这是一个有效的案例。如果 A
的构造函数是 explicit
,从 {0}
到 int
的自动转换就会失败,从而不会产生歧义。
使用 GCC8 可以很好地编译代码。
这应该不是模棱两可的称呼。对于B
的copy/move构造函数被调用,那么对于B b({0});
需要以下步骤:
- 通过
A::A(int)
从 - 从
A
由B::B(A)
在步骤 1 中构建的 - 从
B
由B
. 的 copy/move 构造函数在步骤 2 中构造
0
构建A
B
b
这意味着需要两个用户定义的转换(步骤#1 和#2),但这在一个隐式转换序列中是不允许的。
给定一个 class、A
和一个用户定义的构造函数:
struct A
{
A(int) {}
};
和另一个 B
,接受 A
作为构造函数参数:
struct B
{
B(A) {}
};
然后为了执行如下初始化:
B b({0});
编译器必须考虑以下候选者:
B(A); // #1
B(const B&); // #2
B(B&&); // #3
试图找到从 {0}
到每个参数的隐式转换序列。
请注意 B b({0})
不进行列表初始化 b
--(复制)列表初始化适用于构造函数参数本身。
由于参数是一个初始化列表,将参数与参数匹配所需的隐式转换序列是根据列表初始化序列定义的 [over.ics.list]/p1:
When an argument is an initializer list ([dcl.init.list]), it is not an expression and special rules apply for converting it to a parameter type.
上面写着:
[...], if the parameter is a non-aggregate class X and overload resolution per 13.3.1.7 chooses a single best constructor of X to perform the initialization of an object of type X from the argument initializer list, the implicit conversion sequence is a user-defined conversion sequence with the second standard conversion sequence an identity conversion. If multiple constructors are viable but none is better than the others, the implicit conversion sequence is the ambiguous conversion sequence. User-defined conversions are allowed for conversion of the initializer list elements to the constructor parameter types except as noted in 13.3.3.1.
要使#1 可行,以下调用必须有效:
A a = {0};
这是正确的,因为 [over.match.list]/p1:
— If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class
T
and the argument list consists of the elements of the initializer list.
即,class A
有一个接受 int
参数的构造函数。
要使 #2 成为有效候选者,以下调用必须有效:
const B& b = {0};
When a parameter of reference type is not bound directly to an argument expression, the conversion sequence is the one required to convert the argument expression to the referenced type according to [over.best.ics]. Conceptually, this conversion sequence corresponds to copy-initializing a temporary of the referenced type with the argument expression. Any difference in top-level cv-qualification is subsumed by the initialization itself and does not constitute a conversion.
转换为:
B b = {0};
再次关注[over.ics.list]/p6:
User-defined conversions are allowed for conversion of the initializer list elements to the constructor parameter types [...]
允许编译器使用用户定义的转换:
A(int);
将参数 0
转换为 B
的构造函数参数 A
。
对于候选人#3,同样的推理适用于#2。最终,编译器无法在上述隐式转换序列之间进行选择 {citation needed},并报告歧义。