2015-02-06 19 views
14

W C++ 11, nowa uniwersalna składnia inicjalizacji może być również użyta do wywołania normalnego konstruktora (który nie przyjmuje parametru initializer_list). Chociaż patrząc na to nie jest źle, myślę, że może to spowodować problem w rzeczywistym użyciu.Uniwersalna inicjalizacja C++ 11 powoduje nieoczekiwaną inicjalizację?

Więc załóżmy w moim projekcie używam biblioteki, która pochodzi z następujących klas:

class Foo 
{ 
public: 
    Foo(int size, int value); // create 'size' number of elements 
    Foo(initializer_list<int> list); // create elements as in 'list' 
} 

w projekcie jest używany w ten sposób:

Foo foo{10, 2}; // initialize foo with 2 elements: 10 and 2 

Teraz biblioteka ma nowego release i w nowym wydaniu autor usunął 2 konstruktora, który pobiera initializer_list (albo przez cel lub przez pomyłkę). Nie zauważyłem zmiany, a mój projekt rozwija się szczęśliwie, tak jak poprzednio, tylko z nieoczekiwanym zainicjowaniem foo (teraz jest to 10 elementów zamiast 2).

Inną wersją tego problemu jest to, że Foo miał tylko pierwszy konstruktor i używasz uniwersalnej składni inicjalizacyjnej do init foo, a teraz autor zdecydował się na dodanie drugiego konstruktora, a to samo powoduje, że foo będzie inicjowane z różnymi elementy bez zauważenia.

Chciałam poznać opinię innych osób na ten temat. Czy to jest prawdziwy problem, czy też zbytnio się martwię? Czy istnieje jakieś rozwiązanie, aby temu zapobiec? Dzięki.

+2

Jest to z pewnością dobry argument przeciwko uważaniu go za "uniwersalny" i próbowaniu go używać powszechnie. Użyj poprawnej nazwy "inicjowanie listy" i używaj jej tylko wtedy, gdy chcesz ją zainicjować z listy. Lub gdy składnia zmusza Cię do np. Inicjalizacja w klasie lub w celu uniknięcia kłopotliwej analizy. (Ale to tylko moja opinia, a nie odpowiedź, bo tak naprawdę nie ma odpowiedzi). –

+5

Scott Meyers bardzo dobrze opisuje to w Effective Modern C++ w punkcie 7 i tak, to jest prawdziwy problem. –

+0

Bardzo fajne pytanie. Czy ktoś może wyjaśnić, dlaczego nawias '()' może zostać pominięty? – Chiel

Odpowiedz

1

Prawdziwym problemem jest to, że interfejs API się zmienił.

Jeżeli konstruktor był

Foo(int size, int value); 

i użyłeś

Foo foo(10, 2); 

i API zostałby zmieniony na

Foo(int value, int size); 

to masz ten sam problem.

+0

Czuję, że są różne. W twoim przykładzie konstruktor zmienił swoje parametry - wyraźnie niekompatybilna zmiana interfejsu API. W moim przykładzie ani parametry, ani implementacje nie zostały zmienione dla żadnego istniejącego konstruktora. Zwłaszcza w przypadku dodawania nowej funkcji do klasy ludzie nie oczekują, że interfejs API został zmieniony w sposób niekompatybilny. –

+0

Możesz mieć ten sam problem bez inicjalizatora w ten sam sposób, po prostu ustaw oryginalny konstruktor 'Foo (size_t size, int value)' i nowy konstruktor 'template Foo (Typy ... wartości) '. Po wywołaniu oryginalnego konstruktora za pomocą 'Foo foo (10, 2)', pasowałoby to teraz do nowego konstruktora. – StenSoft

+0

W rzeczywistości konstruktor szablonu może mieć taki sam efekt. Jak wspomniano powyżej od Chiel i Aggieboy, jeśli C++ wyraźnie zgłasza niejednoznaczności w tych przypadkach i wymaga, problemu można uniknąć. Ale myślę, że może to prowadzić do trudności w używaniu klasy w szablonie. –