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

Согласно здесь, 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
};

person johnbakers    schedule 19.04.2016    source источник


Ответы (5)


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

Z z = 1LL;

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

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

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

Z z = Z(1LL);
person eerorika    schedule 19.04.2016
comment
Обратите внимание, что это вызывает явный конструктор, а также конструктор копирования/перемещения. Z z(1LL); будет вызывать только явный конструктор. - person user253751; 20.04.2016

Нет, они не одинаковы. 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
person Barry    schedule 19.04.2016
comment
blech, ненужное использование униформы init. Используйте прямую инициализацию plix. - person Puppy; 19.04.2016
comment
@Puppy Ты заминусовал меня, потому что я использовал брекеты? Шутки в сторону? - person Barry; 19.04.2016
comment
На самом деле, Страуструп также поощряет инициализаторы фигурных скобок (раздел 6.3.5, Язык программирования C++ - 4-е изд., BS, 2013). - person Francis Straccia; 19.04.2016
comment
@Puppy: Они называют это унифицированной инициализацией, потому что она всегда работает и никогда не ошибается. Почему вы, возможно, хотите избежать этого? - person Kevin; 20.04.2016

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(...) и получите тот же результат).

person Yakk - Adam Nevraumont    schedule 19.04.2016

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

person Francis Straccia    schedule 19.04.2016

Они не одинаковы.

Из стандартного рабочего проекта 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
person Andreas DM    schedule 19.04.2016