C++: choosing a `const char *` vs `std::string` when using delegating constructor

1.2k Views Asked by At

Consider the following:

class Foo {
public:
    Foo (const char *in) {
        printf ("C string constructor called\n");
    }
    Foo (std::string const &in) : Foo(in.c_str()) {
        printf ("C++ string constructor called\n");
    }
};
Foo bar ("I never asked for this");
//C string constructor called

So, a constant string is treated as a const char * one.

But what would change, if we make a std::string constructor "primary"?

Can we expect that an std::string object would be created and passed to corresponding constructor without calling a C-string related one?

class Foo {
public:
    Foo (std::string const &in) {
        printf ("C++ string constructor called\n");
    }
    Foo (const char *in) : Foo(std::string (in)) {
        printf ("C string constructor called\n");
    }
};
Foo bar ("I never asked for this");
//C++ string constructor called
//C string constructor called

Again, the C-string constructor was called first.

Is this behavior described in C++ standard, or is it compiler-related?

Would this work the same way for e.g. templates or overloaded functions?

I compiled with GCC 7.3.0 (MSYS2 x64).

3

There are 3 best solutions below

0
Joseph D. On BEST ANSWER

"I never asked for this" is a string literal which consists of const char elements:

Foo bar ("I never asked for this"); // calls Foo (const char *in)

Thus, Foo (const char *in) will always get selected by overload resolution regardless of the "order" in which you declare your constructors.

As seen in your 2nd example,

Foo (const char *in) : Foo(std::string (in))

The delegating constructor is selected and will call the target constructor, as selected by the only member of the initialization list.

0
Maxim Egorushkin On

There is no such thing as primary constructor in C++.

What you observe is that the delegated to constructor (target constructor) body is executed first. Then the body of the constructor that delegates (delegating constructor).

Delegating constructor:

In this case, the target constructor is selected by overload resolution and executed first, then the control returns to the delegating constructor and its body is executed.

0
Remy Lebeau On

Overload Resolution and Constructor Delegation are two completely different things that do not influence each other at all.

Overload Resolution avoids implicit conversions when possible.

A string literal like "I never asked for this" is a const char[], which decays to const char *. That is an exact match for your const char * constructor, so that is the one that gets called. Calling your std::string constructor with a string literal as input would require an implicit conversion, as the compiler would have construct a temporary std::string object to bind to the std::string const & reference.

Had you written this code instead:

Foo bar (std::string("I never asked for this"));

Or this:

std::string str = "I never asked for this";
Foo bar (str);

Then the std::string const & constructor would be called instead of the const char * constructor, as there is no implicit conversion from std::string to const char *.

How constructors delegate to each other is an implementation detail AFTER the compiler decides which constructor to call.