2016-08-24 10 views
47

Czy tworzenie konstruktora o wielu argumentach ma jakiś (użyteczny) efekt?Jawny konstruktor z wieloma argumentami

Przykład:

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

Cóż, gdyby miał tylko jeden parametr, zapobiegałby konwersji typu niejawnego, ale w tym przypadku, chyba że drugi parametr ma wartość domyślną, nie widzę żadnych użytecznych efektów. – Ziezi

+8

Służy do inicjowania nawiasów klamrowych. – 101010

+0

@ 101010 Czy mógłbyś to rozwinąć? – Ziezi

Odpowiedz

62

Aż do C++ 11, tak, by używać explicit na konstruktor multi-arg.

To zmienia się w C++ 11, z powodu list inicjalizujących. Zasadniczo inicjowanie kopii (ale nie inicjowanie bezpośrednie) z listą inicjalizacyjną wymaga, aby konstruktor nie był oznaczony jako 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 
+1

Akceptuję tę odpowiedź, nawet jeśli @Story Teller po raz pierwszy wprowadził inicjalizację nawiasów klamrowych ze względu na wyraźną wzmiankę o C++ 11. –

+3

@PeterG. Pierwsza odpowiedź powinna mieć znaczenie tylko wtedy, gdy obie odpowiedzi są identyczne. Sheftel to lepsze IMHO. :) – StoryTeller

19

Można by natknąć się na niego do inicjowania mocujący (na przykład w macierzach)

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

Doskonałe odpowiedzi przez @StoryTeller i @Sneftel są główną przyczyną. Jednak, IMHO, ma to sens (przynajmniej ja to robię), jako część przyszłej weryfikacji późniejszych zmian w kodzie. Rozważ swój przykład:

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

Ten kod nie ma bezpośredniej korzyści z explicit.

Jakiś czas później zdecydujesz się dodać wartość domyślną dla c, więc staje się w ten sposób:

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

Kiedy robi to, jesteś koncentrując się na parametrze c - z perspektywy czasu, powinien mieć domyślna wartość. Niekoniecznie koncentrujesz się na tym, czy sama nazwa A powinna być niejawnie skonstruowana. Niestety ta zmiana ponownie powoduje, że explicit jest istotne.

Tak więc, aby przekazać, że ctor jest explicit, może to zapłacić, aby zrobić to po pierwszym napisaniu metody.

+3

Świetne spojrzenie na stronę inżynierii oprogramowania :) – StoryTeller

+1

@StoryTeller Wielkie dzięki! Uwielbiałem twoją odpowiedź, BTW. –

+0

Ale co z przypadkiem, gdy opiekun dodaje tę wartość domyślną i stwierdza, że ​​wynik ** powinien ** być dostępny jako konstruktor konwertujący?Teraz muszą ** usunąć ** to "wyraźne", które było tam na zawsze, a wsparcie techniczne zostanie zalane wywołaniami tej zmiany i spędzić ** godzin ** wyjaśniając, że 'wyraźny' był po prostu szumem, i że usunięcie go jest nieszkodliwy. Osobiście nie jestem zbyt dobry w przewidywaniu przyszłości; dość trudno jest zdecydować, jak powinien wyglądać interfejs ** teraz **. –

2

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 można łatwo zobaczyć, explicit zapobiega użyciu listy initializer wraz z bar funkcji bacause konstruktora struct Bar jest zadeklarowany jako explicit.

Powiązane problemy