Явний конструктор, що приймає кілька аргументів


88

Чи має конструктор, що має кілька аргументів explicit, якийсь (корисний) ефект?

Приклад:

class A {
    public:
        explicit A( int b, int c ); // does explicit have any (useful) effect?
};

Відповіді:


120

Аж до C ++ 11, так, немає жодних причин для використання explicitна конструкторі multi-arg.

Це змінюється в C ++ 11 через списки ініціалізаторів. В основному, для ініціалізації копіювання (але не прямої ініціалізації) зі списком ініціалізаторів потрібно, щоб конструктор не був позначений explicit.

Приклад:

struct Foo { Foo(int, int); };
struct Bar { explicit Bar(int, int); };

Foo f1(1, 1); // ok
Foo f2 {1, 1}; // ok
Foo f3 = {1, 1}; // ok

Bar b1(1, 1); // ok
Bar b2 {1, 1}; // ok
Bar b3 = {1, 1}; // NOT OKAY

5
Я думаю, що ця відповідь була б кращою з поясненнями "Чому я це хочу" або "Коли це корисно".
MateuszL

Відповідь @MateuszL Едгара дає, мабуть, найкращий аргумент, чому вона може бути корисною (і, безсумнівно, заслуговує галочки). Причина його існування , однак, просто в тому, що це логічне розширення існуючої семантики для explicit. Я б особисто не заважав створювати конструктори з декількома аргументами explicit.
Снефтель

31

Ви натрапите на нього для ініціалізації фігурних дужок (наприклад, у масивах)

struct A {
        explicit A( int b, int c ) {}
};

struct B {
         B( int b, int c ) {}
};

int main() {
    B b[] = {{1,2}, {3,5}}; // OK

    A a1[] = {A{1,2}, A{3,4}}; // OK

    A a2[] = {{1,2}, {3,4}}; // Error

    return 0;
}

24

Відмінні відповіді @StoryTeller та @Sneftel - головна причина. Однак, IMHO, це має сенс (принаймні я це роблю), оскільки частина майбутніх перевірок згодом змінює код. Розглянемо ваш приклад:

class A {
    public:
        explicit A( int b, int c ); 
};

Цей код безпосередньо не виграє explicit.

Через деякий час ви вирішили додати значення за замовчуванням для c, тож воно стає таким:

class A {
    public:
        A( int b, int c=0 ); 
};

Роблячи це, ви орієнтуєтесь на cпараметр - ретроспективно він повинен мати значення за замовчуванням. Ви не обов’язково концентруєтеся на тому, чи Aслід самому будуватись неявно. На жаль, ця зміна explicitзнову робить актуальним.

Отже, щоб донести, що це ктор explicit, може заробити це зробити при першому написанні методу.


Але як щодо випадку, коли супровідник додає це значення за замовчуванням і робить висновок, що результат повинен бути доступний як конструктор перетворення? Тепер їм доведеться видалити те, explicitщо було там назавжди, а технічна підтримка буде завалена дзвінками про цю зміну і годинами пояснюватиме, що це explicitбув просто шум, і що його видалення нешкідливо. Особисто я не дуже добре передбачаю майбутнє; досить важко вирішити, яким повинен бути інтерфейс зараз .
Піт Беккер,

@PeteBecker Це хороший момент. Я особисто вважаю, що ці два випадки асиметричні, і що набагато частіше при встановленні параметрів за замовчуванням (або при їх видаленні) мимоволі робиться клас неявно конструктивним, а потім фактично одночасно усвідомлюється, що в ретроспективі це повинно бути так. З огляду на це, це "м'які" міркування, і вони можуть різнитися в залежності від людей / проектів / тощо, або навіть бути предметом смаку.
Ami Tavory

8

Ось мої п’ять центів до цієї дискусії:

struct Foo {
    Foo(int, double) {}
};

struct Bar {
    explicit Bar(int, double) {}
};

void foo(const Foo&) {}
void bar(const Bar&) {}

int main(int argc, char * argv[]) {
    foo({ 42, 42.42 }); // valid
    bar({ 42, 42.42 }); // invalid
    return 0;
}

Як ви можете легко побачити, explicitзаважає використовувати список ініціалізаторів разом із barфункцією, оскільки конструктор з struct Barоголошено як explicit.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.