Ключевое слово explicit
сопровождает либо
- конструктор класса X, который нельзя использовать для неявного преобразования первого (любого единственного) параметра в тип X
C ++ [class.conv.ctor]
1) Конструктор, объявленный без явного спецификатора функции, определяет преобразование типов его параметров в тип своего класса. Такой конструктор называется конвертирующим конструктором.
2) Явный конструктор создает объекты точно так же, как неявные конструкторы, но делает это только там, где явно используется синтаксис прямой инициализации (8.5) или где приведено приведение (5.2.9, 5.4). Конструктор по умолчанию может быть явным конструктором; такой конструктор будет использоваться для выполнения инициализации по умолчанию или инициализации значения
(8.5).
- или функция преобразования, которая рассматривается только для прямой инициализации и явного преобразования.
C ++ [class.conv.fct]
2) Функция преобразования может быть явной (7.1.2), и в этом случае она рассматривается только как пользовательское преобразование для прямой инициализации (8.5). В противном случае определяемые пользователем преобразования не ограничиваются использованием в назначениях.
и инициализации.
Обзор
Явные функции преобразования и конструкторы могут использоваться только для явных преобразований (прямая инициализация или явная операция приведения), в то время как неявные конструкторы и функции преобразования могут использоваться для неявных, а также явных преобразований.
/*
explicit conversion implicit conversion
explicit constructor yes no
constructor yes yes
explicit conversion function yes no
conversion function yes yes
*/
Пример использования структур X, Y, Z
и функций foo, bar, baz
:
Давайте рассмотрим небольшую структуру структур и функций, чтобы увидеть разницу между explicit
и не explicit
преобразованиями.
struct Z { };
struct X {
explicit X(int a); // X can be constructed from int explicitly
explicit operator Z (); // X can be converted to Z explicitly
};
struct Y{
Y(int a); // int can be implicitly converted to Y
operator Z (); // Y can be implicitly converted to Z
};
void foo(X x) { }
void bar(Y y) { }
void baz(Z z) { }
Примеры, касающиеся конструктора:
Преобразование аргумента функции:
foo(2); // error: no implicit conversion int to X possible
foo(X(2)); // OK: direct initialization: explicit conversion
foo(static_cast<X>(2)); // OK: explicit conversion
bar(2); // OK: implicit conversion via Y(int)
bar(Y(2)); // OK: direct initialization
bar(static_cast<Y>(2)); // OK: explicit conversion
Инициализация объекта:
X x2 = 2; // error: no implicit conversion int to X possible
X x3(2); // OK: direct initialization
X x4 = X(2); // OK: direct initialization
X x5 = static_cast<X>(2); // OK: explicit conversion
Y y2 = 2; // OK: implicit conversion via Y(int)
Y y3(2); // OK: direct initialization
Y y4 = Y(2); // OK: direct initialization
Y y5 = static_cast<Y>(2); // OK: explicit conversion
Примеры функций преобразования:
X x1{ 0 };
Y y1{ 0 };
Преобразование аргумента функции:
baz(x1); // error: X not implicitly convertible to Z
baz(Z(x1)); // OK: explicit initialization
baz(static_cast<Z>(x1)); // OK: explicit conversion
baz(y1); // OK: implicit conversion via Y::operator Z()
baz(Z(y1)); // OK: direct initialization
baz(static_cast<Z>(y1)); // OK: explicit conversion
Инициализация объекта:
Z z1 = x1; // error: X not implicitly convertible to Z
Z z2(x1); // OK: explicit initialization
Z z3 = Z(x1); // OK: explicit initialization
Z z4 = static_cast<Z>(x1); // OK: explicit conversion
Z z1 = y1; // OK: implicit conversion via Y::operator Z()
Z z2(y1); // OK: direct initialization
Z z3 = Z(y1); // OK: direct initialization
Z z4 = static_cast<Z>(y1); // OK: explicit conversion
Зачем использовать explicit
функции преобразования или конструкторы?
Конструкторы преобразования и неявные функции преобразования могут вносить неоднозначность.
Рассмотрим структуру V
, конвертируемую в int
, структуру U
, неявно конструируемую из V
и функцию f
, перегруженную для U
и bool
соответственно.
struct V {
operator bool() const { return true; }
};
struct U { U(V) { } };
void f(U) { }
void f(bool) { }
Вызов f
неоднозначен, если передается объект типа V
.
V x;
f(x); // error: call of overloaded 'f(V&)' is ambiguous
Компилятор не знает, использовать ли конструктор U
или функцию преобразования для преобразования объекта V
в тип для передачи в f
.
Если бы конструктор U
или функция преобразования V
были бы explicit
, не было бы никакой двусмысленности, поскольку рассматривалось бы только неявное преобразование. Если оба являются явными, вызов f
с использованием объекта типа V
должен быть выполнен с использованием явного преобразования или операции приведения.
Конструкторы преобразования и неявные функции преобразования могут привести к неожиданному поведению.
Рассмотрим функцию, печатающую некоторый вектор:
void print_intvector(std::vector<int> const &v) { for (int x : v) std::cout << x << '\n'; }
Если бы размер-конструктор вектора не был явным, можно было бы вызвать функцию следующим образом:
print_intvector(3);
Что можно ожидать от такого звонка? Одна строка, содержащая 3
или три строки, содержащие 0
? (Где второй, что происходит.)
Использование явного ключевого слова в интерфейсе класса заставляет пользователя интерфейса быть явным в отношении желаемого преобразования.
Как выразился Бьярн Страуструп (в «Языке программирования C ++», 4-е издание, 35.2.1, стр. 1011), на вопрос, почему std::duration
не может быть неявно построен из простого числа:
Если вы знаете, что имеете в виду, проясните это.