B b1({{{"test"}}});
相当于 B b1(A{std::string{const char*[1]{"test"}}});
16.3.3.1.5 列表初始化序列 [over.ics.list]
4 如果参数类型是字符数组133并且初始化列表有一个单独的元素,该元素是适当类型的字符串字面值(11.6.2),则隐式转换序列为标识转换。
编译器尝试所有可能的隐式转换。 例如,如果我们有以下构造函数的类C:
#include <string>
struct C
{
template<typename T, size_t N> C(const T* (&&) [N]) {}
template<typename T, size_t N> C(const T (&&) [N]) {}
template<typename T=char> C(const T* (&&)) {}
template<typename T=char> C(std::initializer_list<char>&&) {}
};
struct A
{
explicit A(const char *) {}
A(C ) {}
};
struct B
{
B(A) {}
B(B &) = delete;
};
int main( void )
{
const char* p{"test"};
const char p2[5]{"test"};
B b1({{{"test"}}});
}
clang 5.0.0编译器无法决定使用哪个并出现以下错误:
29 : <source>:29:11: error: call to constructor of 'C' is ambiguous
B b1(}});
^~~~~~~~~~
5 : <source>:5:40: note: candidate constructor [with T = char, N = 1]
template<typename T, size_t N> C(const T* (&&) [N])
^
6 : <source>:6:40: note: candidate constructor [with T = const char *, N = 1]
template<typename T, size_t N> C(const T (&&) [N])
^
7 : <source>:7:39: note: candidate constructor [with T = char]
template<typename T=char> C(const T* (&&))
^
15 : <source>:15:9: note: passing argument to parameter here
A(C )
^
但如果我们只保留非初始化列表构造函数,代码就能编译通过。
GCC 7.2会选择 C(const T*(&&)) {}
并进行编译。如果不可用,则选择 C(const T*(&&)[N])
。
MSVC则会出现错误:
29 : <source>(29): error C2664: 'B::B(B &)': cannot convert argument 1 from 'initializer list' to 'A'
explicit
时,GCC 调用A(const char*)
,而 Clang 调用A(std::string)
。 - xskxzr