user997112
user997112

Reputation: 30615

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?

Upvotes: 0

Views: 148

Answers (2)

bolov
bolov

Reputation: 75717

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.

Upvotes: 2

MicroVirus
MicroVirus

Reputation: 5477

First, you need to understand what happens if you explicitly delete the bool constructor. It becomes available in overload selection and when it is the best match it becomes used, giving a compiler error then because it is deleted.

This is different from when you do not define it, in that you can indeed 'stop' boolean values from converting via an implicit conversion plus followed by a (different) constructor of your class X, because it is the best match for a boolean, so implicit conversions stop there.

Consider the usage of your class

X x("abc");

Without the deleted bool constructor, this selects X(const string&).

With the deleted bool constructor, however, this will select the explicit X(bool), which is also deleted so this gives a compiler error. The solution to this, as Kerrek SB said, is to define a constructor for const char * as well.

And indeed, if you don't delete the bool constructor then the following selects the double

X x(true);

via first conversion to int and then conversion to double.

So, you can choose either solution, to delete or not to delete, but you need to be aware of the consequences and the requirements. If you delete it, you need to supply a const char * constructor. If you don't delete it, then you need to live with the fact that bool can be used to construct an instance of your class.

Upvotes: 1

Related Questions