Czy powinienem używać nowej funkcji „auto” C ++ 11, szczególnie w pętlach?

20

Jakie są zalety / wady używania autosłowa kluczowego, szczególnie w przypadku pętli?

for(std::vector<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->something();
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->second->something();
}

for(auto it = x.begin(); it != x.end(); it++ )
{
   it->??
}

Wygląda na to, jeśli nie wiem, czy masz iterator na mapie lub wektor ty nie wiesz, czy użyć firstlub secondczy tylko bezpośrednio właściwości dostępu obiektu, nie?

To przypomina mi debatę C # na temat tego, czy użyć słowa kluczowego var. Mam wrażenie, że do tej pory mam wrażenie, że w świecie C ++ ludzie są gotowi na przyjęcie autosłowa kluczowego bez większego wysiłku niż varw świecie C #. Moim pierwszym instynktem jest to, że lubię znać typ zmiennej, aby wiedzieć, jakie operacje mogę na niej wykonać.

Użytkownik
źródło
3
Czekać! Była walka o to, czy użyć var? Tęsknie za tym.
pdr
21
Co więcej, możesz po prostu użyć for (auto& it : x)(lub bez odniesienia, jeśli chcesz skopiować)
Tamás Szelei
7
Wydaje mi się, że jeśli piszesz pętlę, aby iterować zawartość, xa nawet nie wiesz, co to xjest, nie powinieneś pisać tej pętli w pierwszej kolejności ;-)
nikie
@fish: reguły for-loop oparte na zakresie, ale byłbym pedantyczny i zrobiłem: „for (T & it: x)” zamiast tego, gdy używam pętli for opartych na zakresie, ponieważ uważam, że używanie auto jest mniej pouczające. To rodzaj niewłaściwego użycia auto w mojej głowie.
martiert
Walka o używanie var była trochę głupia, zwłaszcza z perspektywy czasu. Zobacz programowanie kultu Cargo .
Craig,

Odpowiedzi:

38

Motywacje w C ++ są bardziej ekstremalne, ponieważ typy mogą stać się znacznie bardziej skomplikowane i złożone niż typy C # z powodu metaprogramowania i innych rzeczy. autojest szybszy w pisaniu i czytaniu oraz bardziej elastyczny / łatwy w utrzymaniu niż typ jawny. Mam na myśli, czy chcesz zacząć pisać?

boost::multi_map<NodeType, indexed_by<ordered_unique<identity<NodeType>>, hashed_non_unique<identity<NodeType>, custom_hasher>>::iterator_type<0> it

To nie jest nawet pełny typ. Opuściłem kilka argumentów szablonu.

DeadMG
źródło
8
+1 na przykład, ale to także mówi coś o stanie „nowoczesnego” C ++.
zvrba
22
@zvrba: Tak - ogólne funkcje są znacznie potężniejsze niż C #.
DeadMG,
4
po to jest typedef
gbjbaanb
19
@gbjbaanb Nie, po to autojest. Przez projekt. typedefpomaga, ale autopomaga więcej.
Konrad Rudolph
1
typedef unieważnia argument, że „nieużywanie auto daje naprawdę długie typy”
Michael
28

W twoim przykładzie:

for(auto it = x.begin(); it != x.end(); i++)
{
  it->??
}

musi być deklaracja xwidoczna. Dlatego rodzaj itpowinien być oczywisty. Jeśli typ xnie jest oczywisty, wówczas metoda jest za długa lub klasa jest za duża.

Kevin Cline
źródło
7
Ponadto, xjest to bardzo zła nazwa zmiennej do pojemnika. W niektórych sytuacjach najprawdopodobniej możesz po prostu spojrzeć na (semantycznie cenną) nazwę i wywnioskować możliwe operacje.
Maks
@Max: używany tylko xjako ogólny przykład, zwykle używam dość opisowych nazw zmiennych.
Użytkownik
@ Użytkownik Oczywiście nie przypuszczałem, że to był prawdziwy przykład;)
Max
14

Sprzeciw ! Załadowane pytanie.

Czy możesz mi wyjaśnić, dlaczego zawiera trzeci kod ??, a pierwszy i drugi nie? W trosce o sprawiedliwość kod musi mieć następujące brzmienie:

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->???
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->second->???
}

Tam. Ten sam problem, nawet jeśli nie korzystałeś auto.

I we wszystkich przypadkach odpowiedź jest taka sama: kontekst ma znaczenie . Nie możesz w sposób znaczący mówić o kawałku kodu w izolacji. Nawet jeśli nie używałeś szablonów, ale jakiś konkretny typ, problem przesunąłby to tylko gdzie indziej, ponieważ czytelnik twojego kodu musiałby wiedzieć o deklaracji tego typu.

Jeśli użycie autow takich sytuacjach spowoduje, że Twój kod będzie nieczytelny, powinieneś traktować to jako znak ostrzegawczy, że coś jest nie tak z projektem kodu. Oczywiście zdarzają się przypadki, w których istotne są szczegóły niskiego poziomu (na przykład w przypadku operacji bitowych lub starszego interfejsu API), w których wyraźny typ może poprawić czytelność. Ale ogólnie - nie.

Jeśli chodzi o var(ponieważ wyraźnie o tym wspomniałeś), istnieje również szeroki konsensus w społeczności C # w zakresie używania var. Argumenty przeciwko jego stosowaniu są zwykle oparte na błędach .

Konrad Rudolph
źródło
1
Myślę, że chodziło o to, że w przypadku auto nie wiesz, co umieścisz dalej ... czy jest to specyficzne dla Twojego kodu „coś”, czy też jest to rozpakowanie związane z typem danych, aby dostać się do obiektu danych, który ma metodę „coś”
Michael Shaw
1
@Ptolemy I chodzi mi o to: w dwóch pozostałych kodów ty też nie wiem (ogólnie), co umieścić obok: Tjest nieprzezroczysty dla użytkownika jako auto. Ale jedno powinno być w porządku, a drugie nie ?! To nie ma sensu. W przypadku OP Tjest to stand-in dla dowolnego typu. W prawdziwym kodzie może to być użycie szablonów (for typename std::vector<T>::iterator…)lub interfejsu klasowego. W obu przypadkach rzeczywisty typ jest ukryty przed użytkownikiem, a mimo to rutynowo piszemy taki kod bez problemów.
Konrad Rudolph
1
Właściwie tak. Jeśli jest to wektor, wiesz, że musisz to zrobić ->, a następnie masz dostęp do swojego typu danych. Jeśli jest to mapa, wiesz, że musisz zrobić -> drugi->, a następnie masz dostęp do swojego typu danych, jeśli jest to auto, nie wiesz, co musisz zrobić, aby uzyskać dostęp do swojego typu danych. Wygląda na to, że mylisz „jaki jest typ danych zawartych w kolekcji STL” z „jakim typem kolekcji STL mamy”. auto pogarsza ten problem.
Michael Shaw
1
@Ptolemy Wszystkie te argumenty są tak samo prawdziwe podczas używania auto. To proste, aby zobaczyć, jakie operacje xobsługują z kontekstu. W rzeczywistości ten typ nie zawiera żadnych dodatkowych informacji: w obu przypadkach potrzebujesz trochę dodatkowej wiedzy (IDE, dokumentacji, wiedzy / pamięci), aby podać zestaw obsługiwanych operacji.
Konrad Rudolph
1
@Ptolemy To jest tylko prawda, jeśli jesteś w bardzo zawiłe sytuacji, że nie wiem, co beginpowraca, ale nie wiem co std::vector<>::iteratorjest. I musisz użyć złego narzędzia programistycznego, które nie może dać ci tych informacji w sposób trywialny. To jest bardzo skomplikowane. W rzeczywistości znasz jedno lub drugie begini iteratorpowinieneś używać IDE lub edytora, który może łatwo udostępnić ci odpowiednie informacje. Każdy nowoczesny edytor IDE i programista może to zrobić.
Konrad Rudolph
11

ZAWODOWIEC

Twój kod :

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

nie będzie się kompilować z powodu nazwy zależnej od szablonu.

Oto poprawna składnia:

for( typename std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

Teraz spójrz, jak długo trwa deklaracja typu. To wyjaśnia, dlaczego autosłowo kluczowe zostało wprowadzone. To:

for( auto it = x.begin(); it != x.end(); i++)

jest bardziej zwięzły. To jest zawodowiec.


KON

Musisz być trochę ostrożny. Za pomocą słowa kluczowego autootrzymujesz zadeklarowany typ.

Na przykład :

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto it : v )
{
  ++ it;   // ops modifying copies of vector's elements
}

vs

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto & it : v )   // mind the reference
{
  ++ it;   // ok, vector's elements modified
}

Podsumowując: tak, powinieneś to zrobić, ale nie nadużywaj go. Niektórzy ludzie używają go zbyt często i wszędzie ustawiają auto, jak w następnym przykładzie:

auto i = 0;

vs

int i = 0;
BЈовић
źródło
auto i = 0. Winny. Robię to. Ale to dlatego, że wiem, że 0jest to literał typu int. (i stała ósemkowa ;-))
Laurent LA RIZZA
6

Tak, powinieneś! autonie usuwa tego typu; nawet jeśli „nie wiesz”, co to x.begin()jest, kompilator wie i zgłosi błąd, jeśli spróbujesz nieprawidłowo użyć tego typu. Ponadto emulacja za mappomocą a nie jest niczym niezwykłym vector<pair<Key,Value>>, więc użycie kodu autobędzie działać dla obu reprezentacji słownikowych.

zvrba
źródło
4

Tak, powinieneś użyć autojako domyślnej reguły. Ma surowe zalety w porównaniu z jawnym określaniem typu:

  • Nie powoduje, że wpisujesz rzeczy, o których kompilator jest już świadomy.
  • Sprawia, że ​​typ zmiennych „śledzi” każdą zmianę typów wartości zwracanych.
  • Dzięki temu zapobiega cichemu wprowadzaniu niejawnych konwersji i krojenia w inicjalizację zmiennych lokalnych.
  • Eliminuje to potrzebę pewnych jawnych obliczeń typu w szablonach.
  • Eliminuje to konieczność nazywania typów zwrotów długimi nazwami. (te, które kopiujesz i wklejasz z diagnostyki kompilatora)

Tutaj masz wybór. Są też przypadki, w których nie masz wyboru:

  • Pozwala na deklarację zmiennych typów nieprzekraczalnych, takich jak typ lambda.

Pod warunkiem, że dokładnie wiesz, co autorobi, nie ma żadnych wad.

Laurent LA RIZZA
źródło