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?
};
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?
};
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
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. –
@PeterG. Pierwsza odpowiedź powinna mieć znaczenie tylko wtedy, gdy obie odpowiedzi są identyczne. Sheftel to lepsze IMHO. :) – StoryTeller
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;
}
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.
Świetne spojrzenie na stronę inżynierii oprogramowania :) – StoryTeller
@StoryTeller Wielkie dzięki! Uwielbiałem twoją odpowiedź, BTW. –
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 **. –
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
.
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
Służy do inicjowania nawiasów klamrowych. – 101010
@ 101010 Czy mógłbyś to rozwinąć? – Ziezi