Czy deklarowanie zmiennych za pomocą auto w C ++ ma jakąś wadę?


143

Wygląda na to, że autobyła to dość istotna funkcja do dodania w C ++ 11, która wydaje się być zgodna z wieloma nowszymi językami. Podobnie jak w przypadku języka takiego jak Python, nie widziałem żadnej jawnej deklaracji zmiennej (nie jestem pewien, czy jest to możliwe przy użyciu standardów Pythona).

Czy istnieje wada używania autodo deklarowania zmiennych zamiast ich jawnego deklarowania?


1
Zobacz też: stackoverflow.com/questions/6434971/… , stackoverflow.com/questions/15254461/… , stackoverflow.com/questions/6900459/… , stackoverflow.com/questions/8430053/is-c11-auto-type-dangerous i być może inne. Nie jestem pewien, czy są to dokładne duplikaty, ale zdecydowanie są kandydatami.
Cody Gray

3
Jedynym minusem, jaki znalazłem, było przeniesienie kodu źródłowego na platformę (konsolową), której kompilator nie obsługiwał (i nie miał zamiaru wspierać) funkcji C ++ 11!
Sam

7
Tylko dla kompletności GotW # 94 "Prawie zawsze auto": Herbsutter.com/2013/08/12/...
Richard Critten

1
Słuchałem cppcast i była wzmianka o inicjatorach clash b / w auto i list. Spróbuję znaleźć ten podcast.
Abhinav Gauniyal

2
moim zdaniem pierwszym minusem jest wpływ na czytelność kodu
ggrr

Odpowiedzi:


111

Pytałeś tylko o wady, więc podkreślam niektóre z nich. Dobrze stosowany automa również kilka zalet. Wady wynikają z łatwości nadużyć i zwiększonego potencjału kodu do zachowywania się w niezamierzony sposób.

Główną wadą jest to, że używając auto, niekoniecznie znasz typ tworzonego obiektu. Istnieją również sytuacje, w których programista może oczekiwać, że kompilator wydedukuje jeden typ, ale kompilator stanowczo wywnioskuje inny.

Biorąc pod uwagę deklarację typu

auto result = CallSomeFunction(x,y,z);

niekoniecznie masz wiedzę o tym, jaki resultto jest typ . Może to być plik int. To może być wskaźnik. To może być coś innego. Wszystkie obsługują różne operacje. Możesz także radykalnie zmienić kod poprzez niewielką zmianę, taką jak

auto result = CallSomeFunction(a,y,z);

ponieważ w zależności od tego, jakie przeciążenia istnieją, dla CallSomeFunction()typu wynik może być zupełnie inny - a kolejny kod może zatem zachowywać się zupełnie inaczej niż zamierzano. Możesz nagle wywołać komunikaty o błędach w późniejszym kodzie (np. Później próba wyłuskiwania int, próba zmiany czegoś, co jest teraz const). Bardziej złowrogą zmianą jest to, że Twoja zmiana przepływa obok kompilatora, ale późniejszy kod zachowuje się w inny i nieznany - prawdopodobnie błędny - sposób.

Brak wyraźnej wiedzy o typie niektórych zmiennych utrudnia zatem rygorystyczne uzasadnienie twierdzenia, że ​​kod działa zgodnie z przeznaczeniem. Oznacza to więcej wysiłku, aby uzasadnić twierdzenia o „przydatności do celu” w dziedzinach o wysokim stopniu krytyczności (np. Krytycznych dla bezpieczeństwa lub krytycznych dla misji).

Inną, bardziej powszechną wadą, jest pokusa dla programisty, aby użyć go autojako tępego narzędzia do wymuszenia kompilacji kodu, zamiast myśleć o tym, co robi kod i pracować nad tym, aby to dobrze.


58
Warto zauważyć, że jeśli takie przykłady są wadą używania auto, to większość języków pisanych na kaczce ma taką wadę z założenia!
Leben Asa

11
Jeśli CallSomeFunction()zwraca inny typ w zależności od sekwencji argumentów, jest to wada projektu CallSomeFunction(), a nie problem auto. Jeśli nie przeczytasz dokumentacji funkcji, której używasz, przed jej użyciem, jest to wada programisty, a nie problem auto. - Ale rozumiem, że grasz tutaj adwokata diabła, po prostu Nir Friedman ma o wiele lepszy argument.
DevSolar

16
@DevSolar: Dlaczego miałby T CallSomeFunction(T, int, int)być defekt projektu? Oczywiście „zwraca inny typ w zależności od kolejności argumentów”.
MSalters

9
„Główną wadą jest to, że używając auto, niekoniecznie znasz typ tworzonego obiektu”. Czy możesz wyjaśnić, dlaczego jest to problem z autotymczasowymi podwyrażeniami, a nie z nimi? Dlaczego jest auto result = foo();źle, ale foo().bar()nie?
Angew nie jest już dumny z SO

24
Z komentarzy wynika, że ​​„wada” jest interpretowana jako przyczyna niedopuszczalności czegoś. Wadą funkcji języka jest wada, którą programista musi wziąć pod uwagę i uzasadnić akceptację lub nie - tj. Dokonywanie kompromisów inżynieryjnych. Nie składam ogólnych twierdzeń dotyczących tego, dlaczego ta funkcja powinna lub nie powinna być używana.
Peter

76

W zasadzie nie jest to wada auto, ale w praktyce wydaje się to być problemem dla niektórych. Zasadniczo niektórzy ludzie albo: a) traktują autojako zbawiciela dla typów i wyłączają mózg, gdy go używają, lub b) zapominają, że autozawsze dedukuje do typów wartości. To powoduje, że ludzie robią takie rzeczy:

auto x = my_obj.method_that_returns_reference();

Ups, właśnie głęboko skopiowaliśmy jakiś obiekt. Często jest to błąd lub awaria wydajności. Następnie możesz też wychylić się w drugą stronę:

const auto& stuff = *func_that_returns_unique_ptr();

Teraz masz wiszące odniesienie. Te problemy wcale nie są powodowane przez auto, więc nie uważam ich za uzasadnione argumenty przeciwko temu. Ale wydaje się, że autosprawia, że ​​ten problem jest bardziej powszechny (z mojego osobistego doświadczenia), z powodów, które wymieniłem na początku.

Myślę, że w miarę upływu czasu ludzie dostosują się i zrozumieją podział pracy: autowyprowadza podstawowy typ, ale nadal chcesz pomyśleć o referencji i stałości. Ale to zajmuje trochę czasu.


Dlaczego na początku możesz głęboko skopiować kosztowny obiekt?
Laurent LA RIZZA

3
@LaurentLARIZZA: Niektóre klasy mają konstruktory kopiujące tylko dlatego, że są czasami potrzebne (np. Instancje std::vector). Kosztowność kopiowania nie jest właściwością klasy, ale poszczególnych obiektów. method_that_returns_referenceMoże więc odnosić się do obiektu klasy, który ma konstruktora kopiującego, ale który jest dość drogi do skopiowania (i nie można go przenieść).
Marc van Leeuwen

@MarcvanLeeuwen: Jeśli obiekt jest drogi do skopiowania i nie można go przenieść, dlaczego miałby być przechowywany w pliku std::vector? (Ponieważ może tak, lub dlatego, że nie kontrolujesz klasy, ale nie o to chodzi). Jeśli kopiowanie jest drogie (i nie posiada żadnego zasobu, ponieważ można go skopiować), dlaczego nie użyć COW na obiekcie? Lokalność danych jest już zabijana przez rozmiar obiektu.
Laurent LA RIZZA

2
@LaurentLARIZZA Nie jest to przypadek, gdy coś przechowywanego w wektorze jest drogie, tylko zwykły wektor, np. <double>, jest drogi do skopiowania, jest to alokacja sterty + O (N) praca. Poruszanie się to czerwony śledź. Pierwsza linia, którą pokazałem, będzie kopiować, a nie przesuwać, chyba że zwrócone odniesienie jest odniesieniem do wartości r. COW tak naprawdę nie ma tu ani tam. Faktem jest, że drogie do kopiowania obiekty będą zawsze istnieć.
Nir Friedman

4
@Yakk Nie można tego bezpiecznie zrobić, ponieważ może ciąć. Jedyną bezpieczną rzeczą, jaką może zrobić, jest = deleteto przeciążenie. Chociaż bardziej ogólnie to, co mówisz, jest rozwiązaniem. To jest temat, który zbadałem, jeśli jesteś zainteresowany: nirfriedman.com/2016/01/18/… .
Nir Friedman,

51

Inne odpowiedzi wspominają o wadach, takich jak „tak naprawdę nie wiesz, jaki jest typ zmiennej”. Powiedziałbym, że jest to w dużej mierze związane z niechlujną konwencją nazewnictwa w kodzie. Jeśli twoje interfejsy są wyraźnie nazwane, nie powinieneś przejmować się dokładnym typem. Jasne, auto result = callSomeFunction(a, b);niewiele ci mówi. Ale auto valid = isValid(xmlFile, schema);mówi wystarczająco dużo, aby użyć validbez martwienia się, jaki jest jego dokładny typ. W końcu przy zwykłym if (callSomeFunction(a, b))też nie znałbyś tego typu. To samo z innymi obiektami tymczasowymi podwyrażeń. Więc nie uważam tego za prawdziwą wadę auto.

Powiedziałbym, że jego główną wadą jest to, że czasami dokładny typ zwrotu nie jest tym, z czym chcesz pracować. W efekcie czasami rzeczywisty typ zwracany różni się od „logicznego” typu zwracanego ze względu na szczegóły dotyczące implementacji / optymalizacji. Najlepszym przykładem są szablony wyrażeń. Powiedzmy, że mamy to:

SomeType operator* (const Matrix &lhs, const Vector &rhs);

Logicznie rzecz biorąc, spodziewalibyśmy SomeTypesię , że tak będzie Vectori zdecydowanie chcemy traktować to jako takie w naszym kodzie. Jednak możliwe jest, że dla celów optymalizacji biblioteka algebry, której używamy, implementuje szablony wyrażeń, a rzeczywisty typ zwracania jest następujący:

MultExpression<Matrix, Vector> operator* (const Matrix &lhs, const Vector &rhs);

Otóż, problem polega na tym MultExpression<Matrix, Vector>, że najprawdopodobniej będzie przechowywać a const Matrix&i const Vector&wewnętrznie; oczekuje, że przekształci się w a Vectorprzed końcem pełnego wyrażenia. Jeśli mamy ten kod, wszystko jest w porządku:

extern Matrix a, b, c;
extern Vector v;

void compute()
{
  Vector res = a * (b * (c * v));
  // do something with res
}

Gdybyśmy jednak autotutaj skorzystali , moglibyśmy mieć kłopoty:

void compute()
{
  auto res = a * (b * (c * v));
  // Oops! Now `res` is referring to temporaries (such as (c * v)) which no longer exist
}

3
@NirFriedman Masz rację, jest silny, ale uważam, że automa bardzo niewiele wad, więc trzymam się tej siły. Inne przykłady serwerów proxy itp. Obejmują różne „konstruktory łańcuchów” i podobne obiekty znajdujące się w DSL.
Angew nie jest już dumny z SO

2
Byłem pogryziony przez szablony ekspresji i autowcześniej, szczególnie z biblioteką Eigen. Jest to szczególnie trudne, ponieważ problem często nie pojawia się w kompilacjach do debugowania.
Dan

1
Użycie automoże również ugryźć podczas korzystania z biblioteki macierzy Armadillo , która w dużym stopniu wykorzystuje metaprogramowanie szablonów do celów optymalizacji. Na szczęście programiści dodali funkcję .eval (), której można użyć, aby uniknąć problemów zauto
mtall

2
„Jeśli twoje interfejsy mają wyraźne nazwy, nie powinieneś martwić się, jaki jest dokładny typ”. Twój kompilator nie może sprawdzić poprawności kodu, studiując nazwy zmiennych. To jest cały punkt systemu czcionek. Ślepe omijanie tego jest głupie!
Wyścigi lekkości na orbicie

1
@Angew: Nie stanowi to problemu dla prowizoriów, ponieważ zwykle używasz ich od razu, co autogeneralnie wymaga pewnego rodzaju sprawdzania typu (a rozpryskiwanie się autowszędzie usuwa tego typu bezpieczeństwo, tak jak wszędzie indziej). To nie jest dobre porównanie.
Wyścigi lekkości na orbicie

13

Jedną z wad jest to, że czasami nie można zadeklarować const_iteratorz auto. Otrzymasz zwykły (inny niż stały) iterator w tym przykładzie kodu zaczerpniętego z tego pytania :

map<string,int> usa;
//...init usa
auto city_it = usa.find("New York");

3
Cóż, iteratorw każdym razie otrzymujesz, ponieważ twoja mapa nie jest const. jeśli chcesz przekonwertować go na a const_iterator, albo jawnie określ typ zmiennej jak zwykle lub wyodrębnij metodę, aby mapa była stała w kontekście twojego find. (Wolałbym to drugie. SRP.)
Laurent LA RIZZA

auto city_it = static_cast<const auto&>(map).find("New York")? albo z C ++ 17 auto city_if = std::as_const(map).find("New York").
Dev Null

11

To sprawia, że ​​twój kod jest trochę trudniejszy lub uciążliwy do odczytania. Wyobraź sobie coś takiego:

auto output = doSomethingWithData(variables);

Teraz, aby ustalić typ wyniku, musiałbyś wyśledzić sygnaturę doSomethingWithDatafunkcji.


40
Nie zawsze. auto it = vec.begin();jest dużo łatwiejszy do odczytania niż std::vector<std::wstring>::iterator it = vec.begin();na przykład.
Jonathan Potter

4
Zgoda. To zależy od przypadku użycia. Mogłem powiedzieć bardziej precyzyjnie.
Skam

1
@SeeDart tak, ludzie, którzy używają auto w ten sposób, robią to źle.
lciamp

6
„Wyśledź sygnaturę funkcji”, jeśli to nie jest najechanie myszą lub naciśnięcie klawisza („podążaj za symbolem” / „przejdź do deklaracji” / jak to się nazywa), musisz albo bardziej skonfigurować edytor, albo przełącz się na IDE, które może to zrobić bez konfiguracji ... Twój punkt widzenia jest jednak nadal aktualny.
hyde

6
Zauważyłem to nie w IDE, ale w małych podglądach różnic podczas przeglądania checkins! Z tego powodu w przypadku samochodów są trudniejsze do odczytania.
JDługosz

10

Podobnie jak ten programista, nienawidzę auto. A raczej nienawidzę tego, jak ludzie nadużywają auto.

Jestem (mocną) opinią, że autopomaga ci pisać ogólny kod, a nie redukuje pisanie .
C ++ to język, którego celem jest umożliwienie pisania niezawodnego kodu, a nie minimalizacja czasu programowania.
Jest to dość oczywiste z wielu funkcji C ++, ale niestety kilka nowszych, takich jak autoten, ogranicza pisanie na klawiaturze, co prowadzi ludzi do myślenia, że ​​powinni zacząć leniwie pisać.

W dawnych autoczasach ludzie używali typedefs, co było świetne, ponieważ typedef pozwalało projektantowi biblioteki pomóc ci ustalić, jaki powinien być typ zwrotu, tak aby ich biblioteka działała zgodnie z oczekiwaniami. Kiedy używasz auto, odbierasz tę kontrolę projektantowi klasy i zamiast tego prosisz kompilator o ustalenie , jaki powinien być typ, co usuwa jedno z najpotężniejszych narzędzi C ++ z zestawu narzędzi i grozi złamaniem ich kodu.

Generalnie, jeśli używasz auto, powinno to być spowodowane tym, że twój kod działa dla każdego rozsądnego typu , a nie dlatego, że jesteś po prostu zbyt leniwy, aby zapisać typ, z którym powinien działać. Jeśli używasz go autojako narzędzia pomagającego lenistwu, to w końcu zaczniesz wprowadzać do programu subtelne błędy , zwykle spowodowane niejawnymi konwersjami, które nie miały miejsca, ponieważ użyłeś auto.

Niestety, te błędy są trudne do zilustrowania w krótkim przykładzie tutaj, ponieważ ich zwięzłość czyni je mniej przekonującymi niż rzeczywiste przykłady, które pojawiają się w projekcie użytkownika - jednak łatwo pojawiają się w kodzie z dużą ilością szablonów, który wymaga pewnych niejawnych konwersji miejsce.

Jeśli chcesz mieć przykład, jest tutaj . Jednak mała uwaga: zanim skusisz się, by skoczyć i skrytykować kod: pamiętaj, że wiele znanych i dojrzałych bibliotek zostało opracowanych wokół takich niejawnych konwersji i są one tam, ponieważ rozwiązują problemy, które mogą być trudne, jeśli nie niemożliwe rozwiązać inaczej. Spróbuj znaleźć lepsze rozwiązanie, zanim je skrytykujesz.


3
which was great because typedef allowed the designer of the library to help you figure out what the return type should be, so that their library works as expected. When you use auto, you take away that control from the class's designer and instead ask the compiler to figure out what the type should beIMO to nie jest dobry powód. Aktualne środowiska IDE, na przykład Visual Studio 2015, umożliwiają sprawdzenie typu zmiennej przez najechanie kursorem auto. To jest * dokładnie * to samo co ten typedef.
kurczak Sombrero,

@JameyD: Brakuje Ci kilku kluczowych punktów: (1) Twój argument IDE działa tylko wtedy, gdy typ jest konkretny, a nie oparty na szablonie. IDE prawdopodobnie nie mogą wskazać poprawnego typu w przypadku typów zależnych, np typename std::iterator_traits<It>::value_type. (2) Chodziło o to, że wywnioskowany typ nie musi być „dokładnie taki sam”, jak właściwy typ zamierzony przez poprzedniego projektanta kodu; używając auto, odbierasz projektantowi możliwość określenia prawidłowego typu.
user541686

Mówisz w zasadzie o proxy, o którym już wspomina jedna z odpowiedzi. Szablony wyrażeń i nonsensy wektorów <bool> nie są zwykłym kodem dla większości ludzi. W większości sytuacji nie chcesz niejawnych konwersji, a auto pomaga w tym. Herb Sutter obszernie mówi o zaletach automatyzacji w jednym ze swoich postów na blogu i nie dotyczy to głównie naciśnięć klawiszy, a także nie dotyczy tylko kodu ogólnego. Poza tym pierwszy link, który podałeś, post na blogu to po prostu okropna rada (dlatego głośno go krytykował w sekcji komentarzy).
Nir Friedman

@NirFriedman: "... vector<bool>bzdury" ... przepraszam? Jak myślisz, jak bitsetjest realizowane? A może uważasz, że pojemniki na bity są całkowicie nonsensem ?!
user541686

1
@NirFriedman: Nic o wektorze <bool> nie jest dla mnie nowością. To, co próbuję ci powiedzieć i że rażąco odmawiasz zrozumienia, to że dla celów tego pytania zestaw bitów nie różni się od wektora <bool> - oba używają proxy, ponieważ proxy zostały uznane za przydatne , a fakt, że serwery proxy są przydatne, jest rzeczywistością, którą musisz zaakceptować, zamiast żyć w zaprzeczaniu. Czy możesz przestać przekształcać to w debatę o tym, czy uważasz, że proxy są przydatne? To nie jest temat debaty, a także twoja opinia na ich temat jest tylko twoją opinią, a nie jakimś faktem.
user541686

6

autonie ma wady per se , i opowiadają się (ręcznie wavily) używać go wszędzie w nowym kodem. Pozwala kodowi na konsekwentne sprawdzanie typu i konsekwentne unikanie cichego cięcia. (Jeśli Bpochodzi od, Aa funkcja zwracająca Anagle zwraca B, to autozachowuje się zgodnie z oczekiwaniami, przechowując swoją wartość zwracaną)

Chociaż starszy kod sprzed C ++ 11 może opierać się na niejawnych konwersjach wywołanych użyciem jawnie wpisanych zmiennych. Zmiana jawnie wpisanej zmiennej na automoże zmienić zachowanie kodu , więc lepiej zachowaj ostrożność.


Głosowanie w dół jest sprawiedliwe, ale czy mógłbyś skomentować dlaczego?
Laurent LA RIZZA

Nie przegłosowałem cię, ale automa on wady jako takie (a przynajmniej - wielu uważa, że ​​tak). Rozważmy przykład podany w drugim pytaniu w tej dyskusji panelowej z Sutter, Alexandrescu i Meyers: Jeśli masz auto x = foo(); if (x) { bar(); } else { baz(); }i foo()zwraca bool- co się stanie, jeśli foo()zmiany zwracają wyliczenie (trzy opcje zamiast dwóch)? autoKod będzie nadal działać, ale nieoczekiwane rezultaty.
einpoklum

@einpoklum: Czy używanie boolzamiast autozmiany cokolwiek w przypadku wyliczenia bez zakresu? Mogę się mylić (nie mogę tego sprawdzić tutaj), ale myślę, że jedyną różnicą jest to, że konwersja do boolodbywa się przy deklaracji zmiennej zamiast przy ocenie warunku w if. Jeśli enumma zakres, konwersja do boolnie nastąpi bez wyraźnego powiadomienia.
Laurent LA RIZZA

4

Słowo kluczowe autopo prostu wywnioskuje typ z wartości zwracanej. Dlatego nie jest odpowiednikiem obiektu Pythona, np

# Python
a
a = 10       # OK
a = "10"     # OK
a = ClassA() # OK

// C++
auto a;      // Unable to deduce variable a
auto a = 10; // OK
a = "10";    // Value of const char* can't be assigned to int
a = ClassA{} // Value of ClassA can't be assigned to int
a = 10.0;    // OK, implicit casting warning

Ponieważ autojest wywnioskowany podczas kompilacji, nie będzie miał żadnych wad w czasie wykonywania.


1
tak, w zasadzie robi to, co robi type()w Pythonie. Wyprowadza typ, ale nie tworzy nowej zmiennej tego typu.
lciamp

2
@lciamp Właściwie to byłoby decltype. autosłuży specjalnie do przypisywania zmiennych.
Sześcienny

4

To, o czym nikt tu dotąd nie wspomniał, ale samo w sobie jest warte odpowiedzi, gdybyś mnie zapytał.

Ponieważ (nawet jeśli wszyscy powinni być tego świadomi C != C++) kod napisany w C można łatwo zaprojektować, aby zapewnić podstawę dla kodu C ++, a zatem być zaprojektowany bez zbytniego wysiłku, aby był zgodny z C ++, może to być wymaganie przy projektowaniu.

Wiem o pewnych regułach, w których niektóre dobrze zdefiniowane konstrukcje Csą nieważne C++i odwrotnie. Ale to po prostu spowodowałoby uszkodzenie plików wykonywalnych i zastosowanie ma znana klauzula UB, która w większości przypadków jest zauważana przez dziwne pętle powodujące awarie lub cokolwiek innego (lub nawet może pozostać niewykryte, ale to nie ma znaczenia tutaj).

Ale autopo raz pierwszy 1 to zmiany!

Wyobraź sobie, że wcześniej użyłeś autojako specyfikatora klasy pamięci i prześlij kod. Nie musiałoby to nawet (w zależności od sposobu użycia) „złamać się”; faktycznie może po cichu zmienić zachowanie programu.

Należy o tym pamiętać.


1 Przynajmniej za pierwszym razem, kiedy jestem tego świadomy.


1
Podczas próby kompilacji i tak wystąpiłby błąd kompilatora.
Kurczak Sombrero

@JameyD: Co by to zrobiło? dlaczego 2 prawidłowe sytuacje kodu o różnym znaczeniu miałyby być kiedykolwiek błędne?
dhein

8
Jeśli polegasz na „żaden typ nie implikuje int” w C, zasługujesz na wszystkie złe rzeczy, które z tego wynikną. A jeśli nie polegasz na tym, użycie autojako specyfikatora klasy pamięci obok typu da ci niezły błąd kompilacji w C ++ (co w tym przypadku jest dobrą rzeczą).
Angew nie jest już dumny z SO

1
@Angew dobrze o to chodzi w przypadku, o którym mówię, tak. Ja tego nie robię. Ale jest to coś, o czym należy przynajmniej pamiętać.
dhein

3

Jednym z powodów, które przychodzą mi do głowy, jest to, że tracisz możliwość przymuszenia klasy, która jest zwracana. Jeśli twoja funkcja lub metoda zwróciła długi 64-bitowy, a chciałeś tylko 32 bez znaku int, tracisz możliwość kontrolowania tego.


1
Istnieje static_cast, a IIRC, na przykład Effective Modern C ++ Meyersa, nawet zaleca użycie go do określenia typu dla zmiennej wpisywanej automatycznie.
hyde

2

Jak opisałem w tej odpowiedzi, auto czasami mogą wystąpić dziwne sytuacje, których nie zamierzałeś. Musisz wprost powiedzieć, auto&że masz typ referencyjny, a robiąc to, po prostu automożesz stworzyć typ wskaźnika. Może to spowodować zamieszanie, jeśli pominie się specyfikator razem, co spowoduje kopię odwołania zamiast rzeczywistego odniesienia.


2
To nie jest fajne. Tak właśnie autojest, nigdy nie wnioskując o referencji ani consttypie. W celach autoinformacyjnych lepiej użyj auto&&. (odniesienie uniwersalne) Jeśli typ nie jest tani do skopiowania lub posiada zasób, to nie powinien być kopiowalny.
Laurent LA RIZZA

1

Kolejny irytujący przykład:

for (auto i = 0; i < s.size(); ++i)

generuje ostrzeżenie ( comparison between signed and unsigned integer expressions [-Wsign-compare]), ponieważ ijest podpisaną int. Aby tego uniknąć, musisz napisać np

for (auto i = 0U; i < s.size(); ++i)

a może lepiej:

for (auto i = 0ULL; i < s.size(); ++i)

1
Tak, ten też mnie irytuje. Ale dziura w języku jest gdzie indziej. Aby ten kod był naprawdę przenośny, zakładając sizezwroty size_t, musiałbyś mieć size_tdosłowne polubienie 0z. Ale możesz zadeklarować UDL, aby to zrobić. ( size_t operator""_z(...))
Laurent LA RIZZA

1
Zastrzeżenia czysto teoretyczne: unsignedprawdopodobnie nie jest wystarczająco duży, aby pomieścić wszystkie wartości std::size_tw głównych architekturach, więc w mało prawdopodobnym przypadku, gdy ktoś miał pojemnik z absurdalnie gigantyczną liczbą elementów, użycie unsignedmoże spowodować nieskończoną pętlę w niższym zakresie indeksów. Chociaż jest to mało prawdopodobne, std::size_tpowinno być używane do uzyskania czystego kodu, który odpowiednio sygnalizuje zamiar. Nie jestem pewien, czy nawet unsigned long longjest ściśle zagwarantowane, że wystarczy, chociaż w praktyce prawdopodobnie musi być to samo.
underscore_d

@underscore_d: tak, słuszna uwaga - unsigned long longgwarantowane jest co najmniej 64 bity, ale size_tprzypuszczam, że teoretycznie może być większe niż to. Oczywiście, jeśli masz> 2 ^ 64 elementów w swoim kontenerze, możesz mieć większe problemy do zmartwień ... ;-)
Paul R

1

Myślę, że autojest dobre, gdy jest używane w zlokalizowanym kontekście, w którym czytelnik łatwo i oczywiście może wywnioskować jego typ, lub dobrze udokumentowane komentarzem tego typu lub nazwą, która określa rzeczywisty typ. Ci, którzy nie rozumieją, jak to działa, mogą przyjąć to w niewłaściwy sposób, na przykład używając go zamiast templatelub podobnie. Moim zdaniem, oto kilka dobrych i złych przypadków użycia.

void test (const int & a)
{
    // b is not const
    // b is not a reference

    auto b = a;

    // b type is decided by the compiler based on value of a
    // a is int
}

Dobre zastosowania

Iteratory

std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int> v();

..

std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int>::iterator it = v.begin();

// VS

auto vi = v.begin();

Wskaźniki funkcji

int test (ClassWithLongName1 a, ClassWithLongName2 b, int c)
{
    ..
}

..

int (*fp)(ClassWithLongName1, ClassWithLongName2, int) = test;

// VS

auto *f = test;

Złe zastosowania

Przepływ danych

auto input = "";

..

auto output = test(input);

Podpis funkcji

auto test (auto a, auto b, auto c)
{
    ..
}

Przypadki trywialne

for(auto i = 0; i < 100; i++)
{
    ..
}

Kiedy chcesz int, musisz wpisać jeszcze jeden znak, jeśli chcesz auto. To nie do przyjęcia
Rerito

@Rerito tak, jest to intrównie dobrze widoczne tutaj, a pisanie intjest krótsze. Dlatego jest to trywialny przypadek.
Khaled.K

0

Dziwię się, że nikt o tym nie wspomniał, ale przypuśćmy, że obliczasz silnię czegoś:

#include <iostream>
using namespace std;

int main() {
    auto n = 40;
    auto factorial = 1;

    for(int i = 1; i <=n; ++i)
    {
        factorial *= i;
    }

    cout << "Factorial of " << n << " = " << factorial <<endl;   
    cout << "Size of factorial: " << sizeof(factorial) << endl; 
    return 0;
}

Ten kod wyświetli to:

Factorial of 40 = 0
Size of factorial: 4

To zdecydowanie nie był oczekiwany rezultat. Stało się tak, ponieważ autowydedukowałem typ zmiennej silnia, intponieważ została do niej przypisana 1.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.