The traditional wisdom is that constructors taking one parameter (explicitly or effectively through the use of default parameters) should be marked explicit
, unless they do define a conversion (std::string
being convertible from const char*
being one example of the latter). You've figured out the reasons yourself, in that implicit conversions can indeed make life harder than it has to be.
A perhaps obvious exception to that would be the copy constructor. Or perhaps another way is to consider that most types are convertible from and to themselves, and that as such the copy constructor is not marked explicit
most of the time.
While it may appear that marking all other kinds of constructors explicit
does not hurt, I'd argue against it. Because while explicit
has no effect on a constructor taking multiple arguments in C++03, it does have an effect in C++11. To put it into code:
struct foo {
explicit foo(int i);
foo(int i, int j);
explicit foo(int i, int j, int k);
};
foo make_foo()
{
/* Not C++11-specific: */
// Error: no conversion from int to foo
return 42;
// Okay: construction, not conversion
return foo(42);
// Okay: constructions
return foo(42, 42);
return foo(42, 42, 42);
/* C++11 specific: */
// Error: no conversion from int to foo
return { 42 };
// Not an error, not a conversion
return { 42, 42 };
// Error! Constructor is explicit
return { 42, 42, 42 };
// Not an error, direct-initialization syntax
return foo { 42, 42, 42 };
}
I personally find it needlessly verbose that in a function that returns foo
I have to explicitly return foo { 42, 42, 42 }
. I don't see what explicit
is protecting me from. I really want the { initializers... }
syntax to mean 'construct object from given initializers', and explicit
gets into the way of that while saving me from nothing. (Since { i }
does boil down to i
in the context of copy-initialization -- most of the time -- I'll gladly give that one up.)
So I'd say get into the habit of using explicit
for unary constructors, and those only.