Why isn't this custom transformation working?
This code doesn't work, but I think the selected transform function should be operator std::string()
, since it takes fewer ( Foo -> std::string
) steps than operator const char*()
( Foo -> const char* -> std::string
).
struct Foo
{
operator const char*();
operator std::string();
};
void f(Foo foo)
{
std::string s2(foo); // Error!
}
The standard states that conversions are limited to a single funnel and that conversions are only applied where they are unambiguous, so why don't they work?
Mistake:
error: call to constructor of 'std::string' (aka 'basic_string<char>') is ambiguous
std::string s2(foo);
^ ~~~
note: candidate constructor
basic_string(basic_string&& __str)
^
note: candidate constructor
basic_string(const basic_string& __str);
^
note: candidate constructor
basic_string(const _CharT* __s, const _Alloc& __a = _Alloc());
^
1 error generated.
source to share
To only use implicit constructors and custom conversions, you must copy-initializing s2
from foo
, direct initialization instead . Quote from cppreference
Direct initialization is more permissive than copy initialization: copy-initialization takes into account implicit constructors and custom conversion functions, while direct initialization considers all constructors and implicit conversion sequences.
Try the following:
std::string s2 = foo;
Your existing code calls the constructor std::string
with an object of type foo
. Since there are valid conversions from foo
to the argument types of multiple constructors std::string
with the same conversion sequence length, you run into ambiguity problems.
source to share
Well, both conversions need one step:
-
foo
→char const*
usestd::string::string(char const*)
-
foo
→std::string
usestd::string::string(std::string&&)
The third overload is an option that should take the overload parameter into account, but a constructor that accepts std::string&&
is better than one that accepts std::string(std::string const&)
, and will be selected if they are the only parameters.
Currently, I cannot easily test it, but I would expect that
std::string s3 = foo;
prefers conversion through std::string
.
source to share
The error is pretty clear. The string that generates the error contains an instance constructor std::string
that has valid constructors using both the const char*
copy constructor and std::string&
. Both require one implicit conversion step, using the overloads you provided, and so it is ambiguous which one is intended.
Explicitly throwing an object to invoke the intended conversion - depending on your internal logic, the constructor const char*
is probably "cheaper", as the conversion std::string
probably just returns std::string((const char*)this)
anyway. If so, I would even recommend removing the transformer std::string
, as it always causes ambiguity that is never more efficient.
source to share