This article explains when we should use the qualifier explicit
and when we shouldn't.
Avoids undesirable conversions
Normally, if a constructor can be called with one argument, it can be used as a conversion. For instance, if you define a constructor, F::F(Foo name)
, and then pass a string
to this function that expects a Foo
, this constructor will be called and convert the string
into a Foo
and will pass the Foo
to your function for you. This can be convenient but is also a source of trouble when things get converted and new objects created without you meaning them to. Declaring a constructor explicit prevents it from being invoked implicitly as a conversion.
In addition to single-parameter constructors, this also applies to constructors where every parameter after the first has a default value, e.g., F::F(Foo name, int id = 42)
.
We require all constructors that are callable with a single argument to be explicit. Always put explicit
in front of such constructors in the class definition: explicit Foo(string name);
Exceptions
Copy and move constructors are exceptions: they should not be explicit
. Classes that are intended to be transparent wrappers around other classes are also exceptions. Such exceptions should be clearly marked with comments.
Finally, constructors that take only a std::initializer_list
may be non-explicit. This permits construction of your type from a braced initializer list, as in an assignment-style initialization, function argument, or return statement. For example:
MyType m = {1, 2};
MyType MakeMyType() { return {1, 2}; }
TakeMyType({1, 2});