Próbuję znaleźć wygodny sposób na zainicjowanie struktur „pod” C ++. Rozważmy teraz następującą strukturę:
struct FooBar {
int foo;
float bar;
};
// just to make all examples work in C and C++:
typedef struct FooBar FooBar;
Jeśli chcę wygodnie zainicjalizować to w C (!), Mógłbym po prostu napisać:
/* A */ FooBar fb = { .foo = 12, .bar = 3.4 }; // illegal C++, legal C
Zauważ, że chcę wyraźnie uniknąć następującego zapisu, ponieważ wydaje mi się, że skręca mi kark, jeśli zmienię cokolwiek w strukturze w przyszłości:
/* B */ FooBar fb = { 12, 3.4 }; // legal C++, legal C, bad style?
Aby osiągnąć to samo (lub przynajmniej podobne) w C ++ jak w /* A */
przykładzie, musiałbym zaimplementować idiotycznego konstruktora:
FooBar::FooBar(int foo, float bar) : foo(foo), bar(bar) {}
// ->
/* C */ FooBar fb(12, 3.4);
Co jest dobre do gotowania wody, ale nieodpowiednie dla leniwych (lenistwo to dobra rzecz, prawda?). Ponadto jest tak samo zły jak /* B */
przykład, ponieważ nie określa wyraźnie, która wartość trafia do którego elementu.
Więc moje pytanie brzmi: jak mogę osiągnąć coś podobnego /* A */
lub lepszego w C ++? Alternatywnie, wystarczyłoby mi wyjaśnienie, dlaczego nie powinienem tego robić (tj. Dlaczego mój paradygmat mentalny jest zły).
EDYTOWAĆ
Przez wygodny mam na myśli również konserwowalne i nienadmiarowe .
źródło
Odpowiedzi:
Wyznaczone inicjalizacje będą obsługiwane w c ++ 2a, ale nie musisz czekać, ponieważ są oficjalnie obsługiwane przez GCC, Clang i MSVC.
GCC Demo MSVC Demo
źródło
Ponieważ
style A
nie jest to dozwolone w C ++ i nie chcesz,style B
to co powiesz na użyciestyle BX
:Przynajmniej w jakimś stopniu pomóc.
źródło
foo
iwbar
przyszłości. C nadal zainicjowałby żądane pola, ale C ++ nie. I o to chodzi w pytaniu - jak osiągnąć ten sam wynik w C ++. Mam na myśli, że Python robi to z nazwanymi argumentami, C - z „nazwanymi” polami, a C ++ też powinien mieć coś, mam nadzieję.explicit FooBar::FooBar(int foo, float bar) : foo(foo), bar(bar)
. Zwróć uwagę na wyraźne słowo kluczowe. Nawet złamanie normy jest lepsze pod względem bezpieczeństwa. In Clang: -Wno-c99-extensionsMożesz użyć lambdy:
Więcej informacji na temat tego idiomu można znaleźć na blogu Herba Suttera .
źródło
fb.XXX = YYY
.Wyodrębnij składowe do funkcji, które je opisują (podstawowa refaktoryzacja):
Wiem, że styl jest bardzo zbliżony do tego, którego nie chciałeś używać, ale umożliwia łatwiejszą zamianę stałych wartości, a także ich wyjaśnienie (dzięki czemu nie trzeba edytować komentarzy), jeśli kiedykolwiek się zmienią.
Inną rzeczą, którą możesz zrobić (ponieważ jesteś leniwy), jest wstawienie konstruktora w tekście, więc nie musisz wpisywać tak dużo (usunięcie "Foobar ::" i czasu spędzonego na przełączaniu między plikiem h i cpp):
źródło
Twoje pytanie jest nieco trudne, ponieważ nawet funkcja:
można nazwać:
ze względu na zasady promocji i konwersji dla wbudowanych typów liczbowych. (C nigdy nie było naprawdę mocno wpisane)
W C ++ to, co chcesz, jest osiągalne, chociaż za pomocą szablonów i statycznych asercji:
W C możesz nazwać parametry, ale nigdy nie przejdziesz dalej.
Z drugiej strony, jeśli chcesz tylko nazwać parametry, piszesz dużo nieporęcznego kodu:
I jeśli chcesz, możesz zabezpieczyć się przed awansem typu.
źródło
Wiele nakładek C ++ kompilatorów (w tym GCC i clang) rozumie składnię inicjalizatora C. Jeśli możesz, po prostu użyj tej metody.
źródło
private: FooBar(float x, int y) {};
Jeszcze innym sposobem w C ++ jest
źródło
inline
!Opcja D:
FooBar FooBarMake(int foo, float bar)
Prawne C, prawne C ++. Łatwa optymalizacja dla POD. Oczywiście nie ma nazwanych argumentów, ale to jest jak we wszystkich C ++. Jeśli potrzebujesz nazwanych argumentów, lepszym wyborem powinien być Cel C.
Opcja E:
Prawne C, prawne C ++. Nazwane argumenty.
źródło
FooBar fb = {};
w C ++, domyślnie inicjalizuje wszystkie składowe struktury.Wiem, że to pytanie jest stare, ale istnieje sposób na rozwiązanie tego problemu, dopóki C ++ 20 w końcu nie przeniesie tę funkcję z C do C ++. Aby rozwiązać ten problem, użyj makr preprocesora z static_asserts, aby sprawdzić, czy inicjalizacja jest prawidłowa. (Wiem, że makra są generalnie złe, ale tutaj nie widzę innego sposobu.) Zobacz przykładowy kod poniżej:
Następnie, gdy masz strukturę z atrybutami const, możesz zrobić to:
Jest to trochę niewygodne, ponieważ potrzebujesz makr dla każdej możliwej liczby atrybutów i musisz powtórzyć typ i nazwę swojej instancji w wywołaniu makra. Nie można również użyć makra w instrukcji return, ponieważ potwierdzenia pojawiają się po inicjalizacji.
Ale to rozwiązuje twój problem: kiedy zmienisz strukturę, wywołanie zakończy się niepowodzeniem w czasie kompilacji.
Jeśli używasz C ++ 17, możesz nawet uczynić te makra bardziej rygorystycznymi, wymuszając te same typy, np .:
źródło
Droga
/* B */
jest w porządku w C ++, również C ++ 0x rozszerzy składnię, więc jest przydatny również dla kontenerów C ++. Nie rozumiem, dlaczego nazywasz to złym stylem?Jeśli chcesz oznaczyć parametry nazwami, możesz użyć biblioteki parametrów boost , ale może to zmylić kogoś nieznającego się na niej.
Zmiana kolejności elementów strukturalnych jest podobna do zmiany kolejności parametrów funkcji. Taka refaktoryzacja może powodować problemy, jeśli nie zrobisz tego bardzo ostrożnie.
źródło
A co z tą składnią?
Właśnie przetestowano na Microsoft Visual C ++ 2015 i g ++ 6.0.2. Działa OK.
Możesz utworzyć określone makro również, jeśli chcesz uniknąć powielania nazwy zmiennej.
źródło
clang++
3.5.0-10 z-Weverything -std=c++1z
wydaje się to potwierdzać. Ale to nie wygląda dobrze. Czy wiesz, gdzie standard potwierdza, że jest to poprawne C ++?ABCD abc = { abc.b = 7, abc.a = 5 };
.Dla mnie najbardziej leniwym sposobem na umożliwienie inicjalizacji inline jest użycie tego makra.
To makro tworzy atrybut i metodę odniesienia do siebie.
źródło