Явный конструктор с несколькими аргументами


88

Имеет ли explicit(полезный) эффект создание конструктора с несколькими аргументами ?

Пример:

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

Ответы:


120

Вплоть до C ++ 11, да, нет причин использовать конструктор explicitс несколькими аргументами.

Это изменилось в 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.
Sneftel

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снова актуально.

Итак, чтобы передать, что ctor есть explicit, это может быть полезно при первом написании метода.


Но как насчет случая, когда сопровождающий добавляет это значение по умолчанию и приходит к выводу, что результат должен быть доступен как конструктор преобразования? Теперь они должны удалить то, explicitчто было там вечно, и техподдержка будет завалена звонками об этом изменении и часами объяснять, что это explicitбыл просто шум, и что его удаление безвредно. Лично я не очень хорошо умею предсказывать будущее; Сейчас довольно сложно решить, как должен выглядеть интерфейс .
Пит Беккер,

@PeteBecker Это хороший аргумент. Я лично считаю, что эти два случая асимметричны, и что гораздо чаще при настройке параметров по умолчанию (или их удалении) непреднамеренно сделать класс неявно конструируемым, а затем в то же время фактически осознать, что ретроспективно это должно быть так. При этом это «мягкие» соображения, которые могут варьироваться в зависимости от людей / проектов / и т. Д., Или даже просто дело вкуса.
Ами Тавори,

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.