Understood how constructors work with the explicit
keyword.
What is the practical application?
If we want to “fence off” unwanted (to us) transformations, then:
class A {
public:
A (int val) {
cout & lt; & lt; "A (int)" & lt; & lt; endl;
}
A (char * val) {
cout & lt; & lt; "A (char *)" & lt; & lt; endl;
}
};
int main ()
{
A a = 'x';
getchar ();
return 0;
}
The result will be: “A (int)”
If we mark both explicit
constructors for fencing purposes, then when:
A a ('x');
we still get the result above. Anyway, unnecessary (to us) transformation will occur.
Answer 1, authority 100%
It is needed not so much to call a specific constructor, but to avoid unnecessary casting (or unambiguous casting).
Roughly – imagine that the constructor of vector
from int
is not explicit
. And we have
void f (vector & lt; & gt; ...);
Then the call to f (5)
will also be a perfectly valid construction. It is unlikely that anyone needs it … Like
vector v;
v = 5;
By the way, Stroustrup writes that it would be nice if all the default constructors were explicit
, and it was necessary to explicitly indicate the refusal of it.
Something like this.
Answer 2, authority 80%
A constructor without explicit
allows you to implicitly convert the type of an argument to the type of the class that owns the constructor.
If we mark both explicit constructors for “fencing” purposes, then with: A a (‘x’); we will still get the result above.
Correct, because the entry A a ('x');
is direct-initialization for which is just the right explicit
constructor. Those. there is an implicit conversion of type char
to type int
for the argument, but conversion to type A
is already quite explicit.
What is the practical application?
Implicit conversions are dangerous, on the one hand they allow you to write fewer code letters , on the other hand, you can get not quite what you expect. Those. Looking at the code, we see one thing, but in the process of execution, something else happens. the implicit conversion happens, oddly enough, implicitly.
For example, in the situation with the already mentioned std :: vector
, a constructor that accepts an integer is marked as explicit
, because this integer specifies the size, although an uninitiated user would expect std :: vector & lt; int & gt; v = 5;
rather than putting the number 5
into a vector, but certainly not creating a vector of five zero-initialized elements. Fortunately, explicit
prevents such an entry from compiling.
Additionally, I want to note that before C++ 11
explicit
, it was relevant to make only constructors that can be called with one argument. With the introduction of uniform initialization , this has become relevant for constructors that require multiple required arguments. For example:
struct A {
/ * explicit * / A (int, double, char) {}
};
A f () {
return {42, 1.5, 'a'};
}
int main () {
A a = {1, 2.0, 'b'};
}
not working yet explicit
. If you make the constructor explicit, you will have to change the code:
A f () {
return A {42, 1.5, 'a'};
}
int main () {
A a = A {1, 2.0, 'b'};
// or A a {1, 2.0, 'b'}; // direct initialization
}
By the way, although only constructors are mentioned in the question, explicit
can be applied to conversion operators as well (starting with C++ 11
):
struct A {
explicit operator int () {return 42; }
};
int main () {
A a;
int j = a; // (1)
int i = static_cast & lt; int & gt; (a); // (2)
bool b = a; // (3)
if (a) {// (4)
// do something
}
else {
// do another
}
}
If operator int
is not marked as explicit
, then all code is fully functional. When we add explicit
, only the string (2)
remains valid. A little special in this case will look like operator bool ()
instead of operator int ()
:
struct A {
explicit operator bool () {return true; }
};
With the explicit
operator, only the (4)
line will work, since using a variable in conditional expressions (if
, while
, ?:
) is interpreted as explicit conversion to bool
. Without explicit
, the code is still fully functional.
Answer 3, authority 47%
In addition to @Harry’s answer, and how to expand your comment:
class Foo
{
public:
operator int () const
{
return 42;
}
};
class Bar
{
public:
Bar (Foo)
{
}
};
void baz (Bar)
{
}
void baz (int)
{
}
int main ()
{
Foo x;
baz (x);
}
Calling the baz
function here will lead to ambiguity for the compiler, since it falls under both baz (int)
and baz (Bar)
. Adding explicit
to the Bar
constructor will give the compiler an unambiguous indication of which function to choose.