Предотвращение нежелательного преобразования в конструкторе


Согласно здесь, explicit:

Задает конструкторы и операторы преобразования (начиная с C++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
};
5 17

5 ответов:

Они не идентичны.

Z z = 1LL;

Вышеописанное работает с неявной версией, но не с явной версией.

Объявление конструктора Z явным не препятствует преобразованию аргумента конструктора из другого типа. Это предотвращает преобразование аргумента в Z без явного вызова конструктора.

Ниже приведен пример явного вызова конструктора.

Z z = Z(1LL);

Нет, это не одно и то же. explicit запрещает неявные преобразования к этому типу, если этот конструктор выбран - неявные преобразования в аргументах не имеют значения. delete запрещает любую конструкцию, если этот конструктор выбран, и может использоваться для запрета неявного преобразования аргумента .

Так, например:

struct X {
    explicit X(int ) { }
};

void foo(X ) { }

foo(4);      // error, because X's constructor is explicit
foo(X{3});   // ok
foo(X{'3'}); // ok, this conversion is fine

, который отделен от deleteING конструктора:

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

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(...) и получите те же выводы).

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;
}

Ваш пример требует явного удаления.

Жить: http://cpp.sh/4sqb

Это не одно и то же.

Из стандартного рабочего проекта n4296:

12.3.1 - [class.conv.ctor]:
1 конструктор, объявленный Без спецификатора функции, явно определяет преобразование из типов его параметры к типу своего класса. Такой конструктор называетсяпреобразующим конструктором .

2 явный конструктор создает объекты точно так же, как и неявные конструкторы, но делает это только там, где синтаксис прямой инициализации (8.5) или где явно используются приведения (5.2.9, 5.4). Конструктор по умолчанию может быть явным конструктором; такой конструктор будет использоваться для выполнения инициализации по умолчанию или valueinitialization (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