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.

      

+3


source to share


3 answers


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.

+3


source


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

.

+1


source


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.

0


source







All Articles