Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

explicit constructor of the wrong type called

In the following sample, my output is "single param ctor", twice. My question is twofold, first, why is the bool ctor called rather than the version containing string, and second, is there a way to get the string version called without casting the call?

As an aside, my actual code doesn't call the constructors with hard-coded strings but with something like:

static constexpr auto NONE = "NONE";
Foo aFoo(NONE);

...

#include <iostream>
#include <string>

using namespace std;


struct Foo {

    explicit Foo(bool _isVisible = false): id(""), isVisible(_isVisible) {cout << "single param ctor" << endl;}
    explicit Foo(const string _id, bool _isVisible = false): id(_id), isVisible(_isVisible) {cout << "multip param ctor" << endl;}

    const string id;
    bool isVisible;
};

int main(int argc, const char * argv[]) {

    shared_ptr<Foo> sharedFoo = make_shared<Foo>("hello");
    Foo regFoo("hi");
    return 0;
}
like image 549
gnichola Avatar asked Sep 20 '16 16:09

gnichola


1 Answers

This is a limitation of C++ inherited from older C++ and ultimately from C.

Your string literals convert to bool better than to the "user-defined" type std::string.

Instead of casting in the call, you could add a constructor that takes const char*, possibly having it delegate to your existing constructor that takes std::string:

explicit Foo(const char* _id, bool _isVisible = false)
   : Foo(std::string(_id), _isVisible)
{};

This constructor will be an "exact match" (or close enough anyway) and prevent bool from stealing the limelight.

Or, since C++14, instead of the cast use the std::string literal, e.g. "hi"s, though you have to remember to do this at the callsite which is a bit "meh" for a general solution.

By the way, if you really want to take your std::string _id parameter by value, don't make it const; you're preventing it from being moved from and necessitating a copy in the member initialiser. Depending on what is going on at your typical callsite, the following may make more sense:

explicit Foo(
   string _id,
   bool _isVisible = false
)
   : id(std::move(_id))
   , isVisible(_isVisible)
{}

If you usually pass in an lvalue, though, at least accept a const std::string& to prevent one copy.

like image 140
Lightness Races in Orbit Avatar answered Oct 27 '22 14:10

Lightness Races in Orbit