Давайте рассмотрим как отличаются явные и неявные конструкторы. Многие посмотрев на код ниже подумают, что различий между инициализацией
a и
b не очень много:
class SomeClass {
public:
SomeClass( int );
};
int main() {
SomeClass a( 5 );
SomeClass b = 5; // если конструктор помечен explicit, то тут будет ошибка
return 0;
}
Я имею ввиду, что в случае инициализации
b не будет вызываться оператор копирования, как думают многие начинающие разработчики. Обе этих строчки вызывают конструктор с той лишь разницей, что в первом случае — это явный(explicit) вызов, в во втором — неявный(implicit).
Cледующий пример поможет продемонстрировать как эта разница может проявиться в виде ошибки на практике:
#include <iostream>
class sss
{
public:
explicit sss( int ) {
std::cout << "int" << std::endl;
}
sss( double ) {
std::cout << "double" << std::endl;
}
};
int main()
{
sss ddd( 7 );
sss xxx = 7;
return 0;
}
Известно, что все целые числа по стандарту автоматически имеют тип
int, если иного явно не указано. Т.е. число 7 в примере выше имеет тип
int. Поэтому, кажется на первый взгляд, что в обоих случаях будет вызван конструктор принимающий
int. Однако, это не так.
Запись вида
«sss ddd( 7 );» является явным вызовом конструктора, а
«sss xxx = 7;» — неявным. Если бы конструктор с
int был в
private секции, то была бы выдана ошибка. Но ключевое слово
explicit скывает конструктор так незаметно, что даже предупреждения компилятор не выдаст. Если скомпилировать и запустить пример, то окажется, что во втором случает будет вызван конструктор, принимающий
double. Это может оказаться довольно неожиданным поворотом событий, если не разбираться в формах вызовов конструкторов. Часто разработчики не придают особого значения как они конструируют объект.
Если написать
explicit для обоих конструкторов, то ошибки можно будет избежать — компилятор подскажет где ошибка. Поэтому
explicit следует писать для всех конструкторов с одним параметром, если специально не предполагается другое поведение. Мне кажется, что это стоило делать по умолчанию в стандарте, а для других случаев ввести что-то вроде
nonexplicit.