2012-04-03 22 views
12

This question wspomniał o oczywistym, idiomatycznym wykorzystaniu C++ 11 w oparciu o zakres.Właściwy styl dla deklaracji w zakresie opartym na zakresie dla

for (auto& elem: container) { 
    // do something with elem 
} 

Mam wątpliwości co do rodzaju odniesienia, którego powinieneś użyć. Wejściowe iteratory mogą zwracać wartości r. Chociaż domyślny typ wprowadzony przez auto mógłby zostać wywnioskowany na const, który wiązałby się z wartością r, to nie wydaje się występować.

Czy najlepszą ogólną praktyką jest stosowanie idealnego przekazywania?

for (auto && elem: container) { 
    // do something with elem 
} 

Nie widzę tutaj żadnej wady, ale wygląda trochę zbyt uroczo. Może nadal nie napisałem wystarczająco dużo C++ 11.

+0

Dla biednych użytkowników MSVC10, którzy nie mogą czerpać korzyści z zakresu, to samo pytanie dotyczy 'BOOST_FOREACH'. –

+0

'auto' nigdy nie jest' const'. Musisz powiedzieć "auto const &". Również 'auto &&' nie jest referencją rvalue, ale raczej "odwołaniem uniwersalnym". –

+0

@KerrekSB Dobra informacja, że ​​'const' nie jest tam wyprowadzona. Nigdy nie powiedziałem nic o referencjach rvalue, v) – Potatoswatter

Odpowiedz

6

Po pierwsze, kilka ogólnych porad dotyczących korzystania z auto, które nie są specyficzne dla zakresu. auto&& może być problematyczne, jeśli inicjalizator jest wartością xvalue odnoszącą się do tymczasowego, ponieważ nie można zastosować w tym przypadku przedłużenia okresu istnienia. Mówiąc prościej, a z kodem:

// Pass-through identity function that doesn't construct objects 
template<typename T> 
T&& 
id(T&& t) 
{ return std::forward<T>(t); } 

// Ok, lifetime extended 
// T {} is a prvalue 
auto&& i = T {}; 

T* address = &i; 

// Still ok: lifetime of the object referred to by i exceed that of j 
// id(whatever) is an xvalue 
auto&& j = id(std::move(i)); 

// No other object is involved or were constructed, 
// all those references are bound to the same object 
assert(&j == address); 

// Oops, temporary expires at semi-colon 
// id(whatever) is an xvalue, again 
auto&& k = id(T {}); 

Dużą wskazówką, że coś jest zacieniony tutaj dzieje jest to, że id ma typ T&& powrócić. Gdyby zwrócono T, wówczas id(whatever) byłaby wartością pryncypium, a zwrócony tymczasowy okres eksploatacji zostałby przedłużony do końca życia (jednak wymagałoby to budowy).


Z iż z drogi, jeśli chodzi o zasięg, bo choć trzeba pamiętać, że for(auto&& ref: init) { /* body */ } określony jest w przybliżeniu równowartość (pomijając pewne szczegóły, które nie mają znaczenia tutaj):

{ 
    using std::begin; 
    using std::end; 
    auto&& range = init; 
    for(auto b = begin(range), e = end(range); b != e; ++b) { 
     auto&& ref = *b; 
     /* body */ 
    } 
} 

Musimy zadać sobie teraz, co zrobić, jeśli *b jest xvalue (czyli typ iterator posiada operator* powrocie value_type&&, jak to ma miejsce np std::move_iterator<Iterator>)? Musi on następnie odwoływać się do obiektu, który będzie odbiegał od linii auto&& ref = *b;. Dlatego jest bezpieczny. W przeciwnym razie, jeśli *b jest wartością pritalną (to znaczy typ iteratora ma wartość operator*, zwracając T dla pewnego typu obiektu T), wówczas czas życia tymczasowego zostaje przedłużony na resztę korpusu pętli. We wszystkich przypadkach jesteś bezpieczny (przypadek, w którym *b jest lwartością, która została pozostawiona ćwiczeniu dla czytelnika).

Osobiście intensywnie korzystam z auto&& z lub bez zasięgu. Ale za każdym razem zadaję sobie pytanie, czy inicjalizator jest wartością x, czy nie, a jeśli tak, to jaki jest czas trwania tego, o czym się mówi.

+0

Dobre informacje. Inna perspektywa ... jedyna tymczasowa, dla której funkcja może zwrócić wartość x, jest argumentem rwartości. 'begin' i' end' nie biorą argumentów, dlatego przesyłanie ich wyników jest bezpieczne. – Potatoswatter

+0

@Potatoswatter W tym przypadku jest to '* b', który jest interesujący (a to nie bierze argumentów, o wiele mniej tymczasowy argument, jak słusznie przeanalizowałeś). 'begin' i' end' biorą argumenty, ale działają tylko z wartościami l. –

+0

Tak, miałem na myśli członków 'begin' i' end' * *, i wyłączając 'this' ponieważ jest to l-wartość. 'operator *' przyjmuje argument w zasadniczo takim samym znaczeniu jak "początek" i "koniec". Ale tak, to naprawdę się liczy. – Potatoswatter