Предотвращение нежелательного преобразования в конструкторе
В соответствии с здесь, explicit
:
Задает конструкторы и операторы преобразования (начиная с С++ 11), которые не допускать неявных преобразований или инициализации копий.
Таким образом, идентичны ли эти два метода?
struct Z {
// ...
Z(long long); // can initialize with a long long
Z(long) = delete; // but not anything smaller
};
struct Z {
// ...
explicit Z(long long); // can initialize ONLY with a long long
};
Ответы
Ответ 1
Они не идентичны.
Z z = 1LL;
Вышеупомянутое работает с неявной версией, но не с явной версией.
Объявление конструктора явного выражения Z
не предотвращает преобразование аргумента конструктора из другого типа. Это предотвращает преобразование из аргумента в Z
без явного вызова конструктора.
Ниже приведен пример явного вызова конструктора.
Z z = Z(1LL);
Ответ 2
Нет, они не то же самое. explicit
запрещает неявные преобразования для этого типа, если этот конструктор выбран - неявные преобразования в аргументах не имеют значения. delete
запрещает любую конструкцию, если этот конструктор выбран, и может использоваться для запрета неявного преобразования аргументов.
Итак, например:
struct X {
explicit X(int ) { }
};
void foo(X ) { }
foo(4); // error, because X constructor is explicit
foo(X{3}); // ok
foo(X{'3'}); // ok, this conversion is fine
Отделяется от delete
конструктора:
struct Y {
Y(int ) { }
Y(char ) = delete;
};
void bar(Y ) { }
bar(4); // ok, implicit conversion to Y since this constructor isn't explicit
bar('4'); // error, this constructor is deleted
bar(Y{'4'}); // error, doesn't matter that we're explicit
Оба метода также ортогональны. Если вы хотите, чтобы тип не был неявно-конвертируемым и строился только с точностью до int
, вы можете сделать оба:
struct W {
explicit W(int ) { }
template <class T>
W(T ) = delete;
};
void quux(W );
quux(4); // error, constructor is explicit
quux('4'); // error, constructor is deleted
quux(4L); // error, constructor is deleted
quux(W{'4'}); // error, constructor is deleted
quux(W{5}); // ok
Ответ 3
explicit
блокирует неявное преобразование в ваш тип.
Ваш метод =delete
блокирует неявное преобразование от long
до long long
.
Они почти не связаны.
Есть 4 случая, которые иллюстрируют разницу:
Z z = 1L;
Z z = 1LL;
является неявным преобразованием из long
и long long
в Z
.
Z z = Z(1L);
Z z = Z(1LL);
- явное преобразование из long
и long long
в Z
.
explicit Z(long long)
:
Z z = 1L;
Z z = 1LL;
в то время как Z(long)=delete
блокирует:
Z z = 1L;
Z z = Z(1L);
explicit Z(long long)
позволяет Z z = Z(1L)
, потому что преобразование из long
в long long
неявно, но не связано с явным преобразованием в Z
, которое происходит потом.
Обратите внимание, что смесь explicit
и =delete
оставляет только Z z=Z(1LL)
как допустимую среди ваших 4 версий.
(приведенное выше предполагает допустимую копию или перемещение ctor, если нет, замените Z z=Z(...)
на Z z(...)
и те же выводы).
Ответ 4
struct Zb {
Zb(long long)
{}; // can initialize with a long long
Zb(long) = delete; // but not anything smaller
};
struct Za {
// ...
explicit Za(long long)
{}; // can initialize ONLY with a long long
};
int main()
{
Za((long long)10); // works
Za((long)10); // works
Zb((long long)10); // works
Zb((long)10); // does not work
return 0;
}
В вашем примере требуется явное удаление.
Live: http://cpp.sh/4sqb
Ответ 5
Они не совпадают.
Из стандартного рабочего чертежа n4296
:
12.3.1 - [class.conv.ctor]:
1 Конструктор, объявленный без явного спецификатора функции, указывает преобразование из типов его параметров к типу его класса. Такой конструктор называется конструктором преобразования.
2 Явный конструктор строит объекты так же, как неявные конструкторы, но делает это только там, где синтаксис прямой инициализации (8.5) или где явно используются приведения (5.2.9, 5.4). Конструктор по умолчанию может быть явным конструктором; такой конструктор будет использоваться для выполнения инициализации по умолчанию или инициализации значения (8.5).
Далее следуют пример каждого из них:
struct X {
X(int);
X(const char*, int =0);
X(int, int);
};
void f(X arg) {
X a = 1; // a = X(1)
X b = "Jessie"; // b = X("Jessie",0)
a = 2; // a = X(2)
f(3); // f(X(3))
f({1, 2}); // f(X(1,2))
}
С явным конструктором:
struct Z {
explicit Z();
explicit Z(int);
explicit Z(int, int);
};
Z a; // OK: default-initialization performed
Z a1 = 1; // error: no implicit conversion
Z a3 = Z(1); // OK: direct initialization syntax used
Z a2(1); // OK: direct initialization syntax used
Z* p = new Z(1); // OK: direct initialization syntax used
Z a4 = (Z)1; // OK: explicit cast used
Z a5 = static_cast<Z>(1); // OK: explicit cast used
Z a6 = { 3, 4 }; // error: no implicit conversion