Please consider the following code:
#include <string.h>
class cstring {
public:
cstring(const char* str) : begin_(str), end_(str ? str + strlen(str) : 0)
{
}
size_t size() const
{
return (size_t)(end_ - begin_);
}
const char* data() const
{
return begin_;
}
private:
const char* begin_;
const char* end_;
};
class Name {
public:
Name(cstring str)
{
}
};
int main()
{
cstring str = "Hello World"; // OK
Name name = str; // OK
Name name2 = "James"; // error
}
Clang will output an error(GCC has similar output):
Source.cpp:37:10: error: no viable conversion from 'const char [6]' to 'Name'
Name name2 = "James"; // error
^ ~~~~~~~
Source.cpp:24:7: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from
'const char [6]' to 'const Name &' for 1st argument
class Name {
^
Source.cpp:26:5: note: candidate constructor not viable: no known conversion from 'const char [6]' to 'cstring' for 1st
argument
Name(cstring str)
^
1 error generated.
In the above code, due to the non-explicit constructors, const char*
can be implicitly converted to cstring
, and cstring
can be implicitly converted to Name
as well; hence the conversion sequence from const char*
to Name
exists. Then why Clang/GCC disallow such a copy-initialization (msvc allows this case)?
Gcc and clang are correct, only one user-defined conversion is allowed in implicit conversion sequence.
Implicit conversion sequence consists of the following, in this order:
zero or one standard conversion sequence;
zero or one user-defined conversion;
zero or one standard conversion sequence.
You could change copy-initialization to direct-initialization as Name name2 ("James");
, in which only one user-defined conversion (from const char*
to cstring
) is required, and the converted cstring
is passed as the argument to the constructor of Name
to construct name2
.
In addition, the implicit conversion in copy-initialization must produce T directly from the initializer, while, e.g. direct-initialization expects an implicit conversion from the initializer to an argument of T's constructor.