Rozważać:
struct Person
{
int height;
int weight;
int age;
};
int main()
{
Person p { .age = 18 };
}
Powyższy kod jest legalny w C99, ale niedozwolony w C ++ 11.
Co to było c ++ 11 uzasadnienie komisji standaryzacyjnej, aby wykluczyć wsparcie dla tak przydatnej funkcji?
OVERLAPPED
Takim przykładem jest Win32 API . Możliwość pisania={.Offset=12345};
sprawiłaby, że kod byłby znacznie bardziej przejrzysty (i prawdopodobnie mniej podatny na błędy). Podobnym przykładem są gniazda BSD.main
C99 jest niezgodny z prawem. Powinien brzmiećstruct Person p = { .age = 18 };
Odpowiedzi:
C ++ ma konstruktory. Jeśli ma sens zainicjowanie tylko jednego elementu członkowskiego, można to wyrazić w programie za pomocą odpowiedniego konstruktora. To rodzaj abstrakcji promowanej przez C ++.
Z drugiej strony wyznaczona funkcja inicjatorów polega bardziej na ujawnianiu i ułatwianiu dostępu do członków bezpośrednio w kodzie klienta. Prowadzi to do takich rzeczy, jak posiadanie osoby w wieku 18 (lat?), Ale o zerowym wzroście i wadze.
Innymi słowy, wyznaczone inicjatory obsługują styl programowania, w którym elementy wewnętrzne są ujawniane, a klient ma elastyczność w decydowaniu, w jaki sposób chce używać tego typu.
C ++ jest bardziej zainteresowany umieszczeniem elastyczności po stronie projektanta typu, więc projektanci mogą ułatwić prawidłowe użycie typu i utrudnić jego niepoprawne użycie. Zapewnienie projektantowi kontroli nad sposobem inicjalizacji typu jest częścią tego: projektant określa konstruktory, inicjatory w klasie itp.
źródło
Person
, którego autor chciał zapewnić użytkownikom jak największą elastyczność w ustawianiu i inicjowaniu elementów członkowskich? Użytkownik może też już pisaćPerson p = { 0, 0, 18 };
(i nie bez powodu).Person
ma konstrukcję bardzo C, więc funkcje C mogą mieć sens. Jednak C ++ prawdopodobnie umożliwia lepszy projekt, który również eliminuje potrzebę wyznaczonych inicjatorów. - Moim zdaniem usunięcie ograniczenia dotyczącego inicjatorów w klasie dla agregatów jest znacznie bardziej zgodne z etosem C ++ niż wyznaczonych inicjatorów.15 lipca 2017 P0329R4 został przyjęty doc ++ 20standard: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf
Zapewnia to ograniczone wsparcie dlac99Wyznaczone inicjatory. To ograniczenie opisano poniżej w C.1.7 [diff.decl] .4, biorąc pod uwagę:
Następujące wyznaczone inicjalizacje, które są ważne w języku C, są ograniczone w języku C ++:
struct A a = { .y = 1, .x = 2 }
jest nieprawidłowy w C ++, ponieważ desygnatory muszą występować w kolejności deklaracji członków danychint arr[3] = { [1] = 5 }
jest nieprawidłowy w C ++, ponieważ inicjalizacja wyznaczona przez tablicę nie jest obsługiwanastruct B b = {.a.x = 0}
jest nieprawidłowy w C ++, ponieważ desygnatory nie mogą być zagnieżdżanestruct A c = {.x = 1, 2}
jest nieprawidłowy w C ++, ponieważ wszystkie lub żadne elementy członkowskie danych nie muszą być inicjowane przez desygnatoryDla c ++ 17a wcześniej Boost faktycznie obsługuje Desygnowanych Intializatorów i było wiele propozycji dodania obsługic ++standard, na przykład: n4172 i propozycja Daryle Walkera, aby dodać oznaczenie do inicjatorów . Propozycje dotyczą implementacjic99Wyznaczone inicjatory w Visual C ++, gcc i Clang:
Ale komisja standaryzacyjna wielokrotnie odrzuca takie propozycje , stwierdzając:
Komentarze Bena Voigta pomogły mi dostrzec nie do przezwyciężenia problemy związane z tym podejściem; dany:
W jakiej kolejności byłyby wywoływane te funkcje c99:
struct X foo = {.a = (char)f(), .b = g(), .c = h()}
? Co zaskakujące, wc99:( Wygląda na to, że Visual C ++, gcc i Clang mają uzgodnione zachowanie, ponieważ wszystkie będą wykonywać wywołania w tej kolejności :)
h()
f()
g()
Ale nieokreślony charakter standardu oznacza, że gdyby te funkcje miały jakąkolwiek interakcję, wynikowy stan programu również byłby nieokreślony, a kompilator nie ostrzegłby cię : Czy istnieje sposób, aby ostrzec o niewłaściwym działaniu wyznaczonych inicjatorów?c ++ nie mają rygorystyczne wymogi inicjalizator-List 11.6.4 [dcl.init.list] 4:
Więc c ++ wsparcie wymagałoby wykonania tego w kolejności:
f()
g()
h()
Łamanie kompatybilności z poprzednimi wersjami c99wdrożenia.
Jak omówiono powyżej, omijano ten problem przez ograniczenia dotyczące wyznaczonych inicjatorów zaakceptowanych w programiec ++ 20. Zapewniają ustandaryzowane zachowanie, gwarantując kolejność wykonywania Wyznaczonych inicjatorów.
źródło
struct X { int c; char a; float b; }; X x = { .a = f(), .b = g(), .c = h() };
wywołaniuh()
odbywa się przed albof()
albog()
. Jeśli definicjastruct X
nie jest w pobliżu, będzie to bardzo zaskakujące. Pamiętaj, że wyrażenia inicjujące nie muszą być wolne od skutków ubocznych.Trochę włamań, więc po prostu udostępniaj dla zabawy.
I używaj go jak:
który rozszerza się do:
źródło
$
typeT
i przypisujesz jej elementy członkowskie bezpośrednio przed jej zwróceniem. Ładne. Zastanawiam się, czy są z tym jakieś problemy z wydajnością.Wyznaczone inicjatory są obecnie zawarte w pracy C ++ 20: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf, więc być może w końcu je zobaczymy!
źródło
&
operator zwróciłby adres, który obiekt będzie miał podczas swojego życia).Dwie podstawowe funkcje C99, których w C ++ 11 brakuje, wymienia „Wyznaczone inicjatory i C ++”.
Myślę, że „wyznaczony inicjator” związany jest z potencjalną optymalizacją. Tutaj jako przykładu używam „gcc / g ++” 5.1.
Wiedzieliśmy w czasie kompilacji, że
a_point.x
wynosi zero, więc mogliśmy się spodziewać, żefoo
zostanie zoptymalizowany do jednegoprintf
.foo
jest zoptymalizowanyx == 0
tylko do drukowania .W przypadku wersji C ++
I to jest wyjście zoptymalizowanego kodu asemblera.
Widzimy, że
a_point
tak naprawdę nie jest to stała czasowa kompilacji.źródło
constexpr point(int _x,int _y):x(_x),y(_y){}
. Optymalizator clang ++ wydaje się eliminować również porównanie w kodzie. Więc to tylko kwestia QoI.struct addrinfo
lubstruct sockaddr_in
, więc zostajesz z zadaniami odrębnymi od deklaracji.