Подтвердить что ты не робот

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

В соответствии с здесь, 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
};
4b9b3361

Ответ 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