I believe this is a clang bug. According to the rules for list-initialization in [dcl.init.list]:
List-initialization of an object or reference of type T is defined as follows:
- If
T is a class type and the initializer list has a single element of type cv U, where U is T or a class derived from T, [...]
- Otherwise, if
T is a character array and [...]
- Otherwise, if
T is an aggregate, [...]
- Otherwise, if the initializer list has no elements [...]
- Otherwise, if
T is a specialization of std::initializer_list<E>, [...]
- Otherwise, if
T is a class type, constructors are considered. The applicable constructors are enumerated
and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see
below) is required to convert any of the arguments, the program is ill-formed.
- [...]
T is a class type, so we consider the basic_string constructors. #7 in that list (the copy constructor) is an applicable, viable constructor and so it should be chosen. At that point, these expressions should be equivalent:
struct Foo {
operator std::string() const { return "hello"; }
};
std::string s{Foo{}}; // error
std::string s(Foo{}); // OK
std::string s = Foo{}; // OK
For some reason though, in the list-initialization case, clang complains that there is:
no known conversion from Foo to const std::__cxx11::basic_string<char> & for 1st argument
There is though, so I filed this as LLVM Bug 23658.