Reputation: 30615
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
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
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