2016-04-19 6 views
17

Согласно here, 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 
}; 

ответ

17

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

Z z = 1LL; 

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

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

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

Z z = Z(1LL); 
+3

Обратите внимание, что это вызывает явный конструктор, а также конструктор copy/move. 'Z z (1LL);' будет вызывать только явный конструктор. – immibis

24

Нет, они не то же самое. 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 

То есть отдельно от 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 
+0

blech, ненужное использование равномерного init. Используйте прямой init plix. – Puppy

+2

@Puppy Ты меня запустил, потому что я использовал брекеты? Шутки в сторону? – Barry

+3

@Puppy Btw, 'W {5}' * is * direct initialization ... – Barry

2
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

1

Они не то же самое.

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

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

Явный конструктор строит объекты так же, как без явных конструкторов, но делает это только тогда, когда синтаксис прямой инициализации (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 
5

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