Search code examples
c++constructorcompiler-construction

Prevent implict object construction from bool?


I have a class, X, and it has the following constructors:

class X{

    X(int64_t, int16_t, bool, int8_t);
    X(int64_t, int16_t, bool);
    X(double);
    X(double, FT);

    explicit X(const string&); 
    X(const string&, Y);
};

The problem is the compiler once created an X object passing just a bool (presume it was the double constructor which allowed this?) and it caused an issue.

To prevent this I made the bool constructor explicit and deleted it:

explicit X(bool) = delete;

but now I get compiler errors:

EXPECT_EQUALS(X("9.8901099"), a/b);
EXPECT_EQUALS(X{"56267"}, x);
X x{"-56267E-1"};
X b("5");

where the compiler says I have made use of deleted function explicit X(bool) = delete

How do I prevent an object of X being created from a bool?


Solution

  • This happens because bool is a better match for const char[] than std::string. When you have no ctor accepting bool then the std::string one is chosen. When you have a ctor with bool then this overload is selected. Only it is deleted so it is illegal to call it.

    Lets see this behavior with a simple free overloaded function:

    auto foo(int) { cout << "foo int" << endl; }
    auto foo(std::string const &) { cout << "foo string" << endl; }
    
    auto main() -> int {
      foo(3);       // int
      foo("Asdf");  // std::string
    }
    

    When you add a bool overload:

    auto foo(int) { cout << "foo int" << endl; }
    auto foo(std::string const &) { cout << "foo string" << endl; }
    auto foo(bool) { cout << "foo bool" << endl; }
    
    auto main() -> int {
      foo(3);      // int
      foo("Asdf"); // bool (also warning implicit conversion turn string literal into bool)
    }
    

    The solution is to add a char const * overload:

    auto foo(int) { cout << "foo int" << endl; }
    auto foo(std::string const &) { cout << "foo string" << endl; }
    auto foo(char const *s) {
       cout << "foo char const *" << endl;
       // possibly:
       return foo(std::string{s});
    }
    auto foo(bool) = delete;
    
    auto main() -> int {
      foo(3);      // int
      foo("Asdf"); // char const *
      foo(true);   // error
    }
    

    Because some legacy code uses and returns char * instead of char const * you might need to add a char * overload too.