5
votes

Consider:

struct Foo
{
    Foo(std::string str) {}
};

struct Bar
{
    Bar(Foo f) {}
};

int main(int argc, char* argv[])
{
    Foo f("test");

    Bar b1(f);
    Bar b2(std::string("test"));
    Bar b3("test");

    return 0;
}

This fails to compile on the declaration of b3 ('cannot convert argument 1 from 'const char [5]' to 'Foo''). Which makes sense, because there is no direct way to convert the const char to a Foo. However, there is a way to convert the const char to a std::string, and then use that to construct a Foo (which is what is happening in b1 and b2), and that is what I want because it makes the API nicer to use (not having to instantiate a Foo or an std::string explicitly every time).

So my question is: is there a way to let the compiler implicitly call the Foo(std::string) copy constructor? In other words, is there a way to make a declaration like that of b3 work, let it be the same as b2, and without declaring a const char* copy constructor for Foo? (that last thing is the obvious way but my real code is of course not as simple as this, and I'd prefer not having to add const char* copy constructors and handling all of the other initialisation in the constructors correctly and keeping that in sync with the std::string copy constructor).

2
An implicit conversion sequence is allowed to only contain one user-defined conversion. You are asking for two (const char* to std::string, then std::string to Foo). One way to go would be to add a Foo constructor overload taking const char*. - Igor Tandetnik
Nitpick: None of the constructors you mention is a copy constructor, they're just constructors. - molbdnilo
Thanks, 'implicit conversion sequence' is the magic string I was looking for. Indeed it is impossible to do what I wanted, I'll have to use an extra constructor. - Roel
Re: nitpick, yes it seems that the standard doesn't call constructors that are considered for copy initialisation 'copy constructors'. I've always done so myself, learned two things from this question I guess :) - Roel

2 Answers

8
votes

If c++11 is acceptable, you could add a delegating constructor to Foo which takes a const char* and just calls the other constructor:

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

Alternatively, you could use c++14's std::string literal:

using namespace::std::string_literals;
Bar b3("test"s);

You could also emulate a delegating constructor by having both constructors call a separate function:

struct Foo
{
    Foo(std::string str) { init(str); }
    Foo(const char* str) { init(str); }
    void init(std::string str) {}
};

The downside of the above is that you'll need to think carefully about anything you were doing in initialization lists that you now need to do in the body of init.

1
votes

what about changing this:

struct Bar
{
    Bar(Foo f) {}
};

to

struct Bar
{
    Bar(Foo f) {}
    Bar(std::string f) {}
};

using polymorphism...