Jawny konstruktor pobierający wiele argumentów


88

Czy tworzenie konstruktora z wieloma argumentami explicitma jakiś (użyteczny) efekt?

Przykład:

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

Odpowiedzi:


120

Aż do C ++ 11, tak, nie ma powodu, aby używać explicitna konstruktorze z wieloma argumentami.

To się zmienia w C ++ 11 z powodu list inicjalizujących. Zasadniczo inicjalizacja kopiowania (ale nie inicjalizacja bezpośrednia) z listą inicjatorów wymaga, aby konstruktor nie był oznaczony explicit.

Przykład:

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
Myślę, że ta odpowiedź byłaby lepsza z wyjaśnieniem „Dlaczego miałbym tego chcieć” lub „Kiedy jest to przydatne”.
MateuszL

@MateuszL Odpowiedź Edgara jest prawdopodobnie najlepszym argumentem za tym, dlaczego może być przydatna (i prawdopodobnie zasługuje na zaznaczenie). Powodem jest to nie jest jednak po prostu dlatego, że to logiczne rozszerzenie istniejących semantyki dla explicit. Osobiście nie zawracałbym sobie głowy tworzeniem konstruktorów wieloskładnikowych explicit.
Sneftel

31

Natknąłbyś się na to podczas inicjalizacji nawiasów klamrowych (na przykład w tablicach)

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

Głównym powodem są doskonałe odpowiedzi udzielone przez @StoryTeller i @Sneftel. Jednak IMHO, ma to sens (przynajmniej ja to robię), jako część przyszłego sprawdzania późniejszych zmian w kodzie. Rozważ swój przykład:

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

Ten kod nie korzysta bezpośrednio z explicit .

Jakiś czas później decydujesz się dodać domyślną wartość dla c, więc staje się ona następująca:

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

Robiąc to, koncentrujesz się na cparametrze - z perspektywy czasu powinien on mieć wartość domyślną. Niekoniecznie koncentrujesz się na tym, czy Asam powinien być skonstruowany w sposób niejawny. Niestety, ta zmiana explicitznów ma znaczenie.

Tak więc, aby pokazać explicit, że ctor jest , warto byłoby to zrobić podczas pierwszego pisania metody.


Ale co w przypadku, gdy opiekun dodaje to domyślne i stwierdza, że ​​wynik powinien być dostępny jako konstruktor konwertujący? Teraz muszą usunąć to explicit, co było tam od zawsze, a pomoc techniczna będzie zalewana wezwaniami na temat tej zmiany i spędzać godziny na wyjaśnianiu, że explicitto tylko hałas, a usuwanie go jest nieszkodliwe. Osobiście nie jestem dobry w przepowiadaniu przyszłości; Trudno zdecydować, co na tyle interfejs powinien wyglądać teraz .
Pete Becker

@PeteBecker To dobra uwaga. Osobiście uważam, że te dwa przypadki są asymetryczne i że przy ustawianiu parametrów jako domyślnych (lub ich usuwaniu) o wiele częściej zdarza się nieumyślnie uczynić klasę niejawnie konstruowalną, a jednocześnie zdać sobie sprawę, że z perspektywy czasu powinno tak być. Biorąc to pod uwagę, są to względy „miękkie” i mogą się różnić w zależności od ludzi / projektów / itp., A nawet mogą być po prostu kwestią gustu.
Ami Tavory

8

Oto moje pięć centów do tej dyskusji:

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;
}

Jak łatwo zauważyć, explicitzapobiega używaniu listy inicjalizującej razem z barfunkcją, ponieważ konstruktor struct Barjest zadeklarowany jako explicit.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.