johnbakers
johnbakers

Reputation: 24771

Why can compiler only implicitly convert char * to std::string in some cases

These work:

struct WithString {
  WithString(std::string){};
};

void takeString(std::string){}

//implicit conversions:
takeString("hello");
WithString("hello");

But this does not:

WithString makeWithString() { return "hello";}

// error: no viable conversion from returned value of type 'const char [6]'...

If "hello" is implicitly converted to std::string in the first two cases, why cannot it not be in the last case? Note that I did not specify the WithString constructor as explicit, so I'd expect such a conversion.

I can get the behavior to work by doing this:

struct WithString {
  WithString(std::string){};
  WithString(const char *){};
};

I'm just curious about this oddity. If I postulate a guess, I would say it is because in the first two working cases, the conversion is between const char * to std::string, but in the error case, this would instead require a chain of 2 conversion, first from const char * to std::string, and then from std::string to WithString. So perhaps that is the reason, but I'm not sure.

Upvotes: 4

Views: 1628

Answers (3)

einpoklum
einpoklum

Reputation: 131986

Your method:

WithString makeWithString() { return "hello";}

needs two conversions: The implicit const char *-to-std::string conversion, then a construction of a WithString object. C++ allows at most one of these to happen implicitly. See also the discussion here:

Non-const copy constructor and implicit conversions on return value

Upvotes: 3

vdovydaitis3
vdovydaitis3

Reputation: 31

Read implicit conversions section in the C++ standard. I tried the following code in VS 2015 and it compiled with no errors.

#include <string>

struct WithString {
    WithString(std::string) {};
};

void takeString(std::string) {}

//implicit conversions:
void someFunc()
{
    takeString("hello");
    WithString("hello");
    WithString t = "hello";
}

WithString makeWithString() { return "hello"; }

It appears that VS2015 is incorrect (treating a conversion from const char* to string as a standard conversion). The following code should work according to the standard, but produces an error in VS2015:

WithString makeWithString() { return "hello"s; }
WithString t = "hello"s;

See also copy initialization. In the Notes it explicitly calls WithString t = "hello"; an error.

Upvotes: -1

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385284

I would say it is because in the first two working cases, the conversion is between const char * to std::string, but in the error case, this would instead require a chain of 2 conversion, first from const char * to std::string, and then from std::string to WithString. So perhaps that is the reason, but I'm not sure.

Exactly.

Without your const char* constructor overload, this:

WithString makeWithString() { return "hello";}

would require two user-defined implicit conversions; one to std::string and another to WithString. That is not possible.

Here, though, there's only one implicit conversion (to std::string):

takeString("hello");

And the same is true here, because the subsequent "conversion" to WithString is explicit:

WithString("hello");

I can get the behavior to work by doing this:

struct WithString {
  WithString(std::string){};
  WithString(const char *){};
};

Yes, that's what you should do.

Upvotes: 6

Related Questions