In C++, if a constructor can be called with a single argument, it is considered implicit by default. Such a constructor can be used for implicit type conversion. To protect against this, the keyword explicit is used.
explicit constructors prevent implicit conversions.Using explicit allows you to avoid unexpected conversions that can occur, for example, when passing a mismatched type argument to functions or when initializing a variable.
Example:
struct Foo { explicit Foo(int x) { /* ... */ } }; Foo a = 10; // Error, explicit prevents implicit initialization Foo b(10); // Ok
If explicit weren't present, then the statement Foo a = 10; would be allowed and could lead to unexpected bugs.
Question: Do all constructors declared with explicit prevent being called during initialization with = ?
Common Answer: Yes, explicit prohibits any initialization with =.
Correct Answer: explicit only prevents implicit conversions. With direct initialization (ClassName obj(param);) the explicit constructor is called. With copy initialization (ClassName obj = param;) — no.
Example:
struct A { explicit A(int) {} }; A x = 1; // Error A y(1); // OK
Story: A project had a constructor written without explicit, allowing initialization through types, which led to unexpected automatic conversions of function arguments. This caused hidden errors and made debugging difficult.
Story: A developer did not set explicit for the container constructor, and the default constructor suddenly began to be called during assignment or when passing other types. The result — incorrect logic for object creation and unpredictable behavior.
Story: An inexperienced programmer declared an explicit constructor but was surprised that direct initialization with parentheses worked, thinking that explicit blocks it as well. This led to the non-use of convenient safe patterns and unnecessary code in the project.