W C++, jeśli konstruktor może być wywołany z jednym argumentem, jest domyślnie uważany za implicit (niejawny). Taki konstruktor może być używany do niejawnej konwersji typów. Aby się przed tym bronić, stosuje się słowo kluczowe explicit.
explicit konstruktorzy zabraniają niejawnych konwersji.Użycie explicit pozwala uniknąć nieoczekiwanych konwersji, które mogą wystąpić, na przykład, przy przekazywaniu do funkcji argumentu innego typu lub przy inicjalizacji zmiennej.
Przykład:
struct Foo { explicit Foo(int x) { /* ... */ } }; Foo a = 10; // Błąd, explicit zabrania niejawnej inicjalizacji Foo b(10); // OK
Gdyby nie było explicit, zapis Foo a = 10; byłby dozwolony i mógłby prowadzić do nieoczekiwanych błędów.
Pytanie: Czy wszystkie konstruktory zadeklarowane jako explicit nie mogą być wywoływane przy inicjalizacji z = ?
Częsta odpowiedź: Tak, explicit zabrania wszelkiej inicjalizacji z =.
Poprawna odpowiedź: explicit zabrania tylko niejawnych konwersji. Z inicjalizacją bezpośrednią (ClassName obj(param);) konstruktor explicit jest wywoływany. Przy inicjalizacji kopią (ClassName obj = param;) — nie.
Przykład:
struct A { explicit A(int) {} }; A x = 1; // Błąd A y(1); // OK
Historia: W projekcie napisano konstruktor bez explicit, umożliwiający inicjalizację przez typy, co prowadziło do nieoczekiwanych automatycznych konwersji argumentów funkcji. Spowodowało to pojawienie się ukrytych błędów i utrudniło debugowanie.
Historia: Programista nie umieścił explicit dla konstruktora kontenera, a konstruktor domyślny nagle zaczął być wywoływany przy przypisaniu lub przekazywaniu innych typów. Efekt — niepoprawna logika tworzenia obiektów i nieprzewidywalne zachowanie.
Historia: Niedoświadczony programista zadeklarował explicit konstruktor, ale był zaskoczony, że inicjalizacja bezpośrednia z okrągłymi nawiasami działa, myśląc, że explicit blokuje i tę. Prowadziło to do nieużywania wygodnych bezpiecznych wzorców i zbędnego kodu w projekcie.