2012-04-03 1 views
12

This question erwähnt die offensichtliche, idiomatische Verwendung von C++ 11 Range-basierte für.Proper Stil für die Deklaration im Bereich-basierte für

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

Ich habe mit Zweifel an der Art Referenz Sie sollen, wenn auch verwenden. Eingabe-Iteratoren können R-Werte zurückgeben. Obwohl der von auto eingeführte implizite Typ auf const zurückgeführt werden könnte, der an einen R-Wert binden würde, scheint dies nicht der Fall zu sein.

Ist die beste allgemeine Praxis, perfekte Weiterleitung zu verwenden?

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

Ich sehe keinen Nachteil hier, aber es sieht ein wenig zu süß. Vielleicht habe ich einfach noch nicht genug C++ 11 geschrieben.

+0

Für die armen MSVC10-Benutzer, die Reichweite nicht genießen können, gilt die gleiche Frage für 'BOOST_FOREACH'. –

+0

'auto' ist nie' const'. Sie müssen 'auto const &' sagen. Außerdem ist 'auto &&' keine rvalue-Referenz, sondern eher eine "universelle Referenz". –

+0

@KerrekSB Gute Info, dass 'const' dort nicht abgeleitet ist. Ich habe nie etwas über rvalue-Referenzen gesagt; v) – Potatoswatter

Antwort

6

Zunächst einige allgemeine Hinweise zur Verwendung von , die nicht spezifisch für den Bereich ist. auto&& kann problematisch sein, wenn der Initialisierer ein xvalue ist, der sich auf einen temporären Wert bezieht, da in diesem Fall die lebenslange Erweiterung nicht angewendet werden kann. Um es einfach auszudrücken, und mit dem Code:

// 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 {}); 

Die große Ahnung, dass es etwas schattig ist hier los ist, dass id zurückkehren muss Typen T&&. Wenn es T zurückgibt, dann wäre id(whatever) ein prvalue, und das zurückgekehrte temporäre hätte seine Lebensdauer verlängert (aber das würde eine Konstruktion beinhalten).


Mit diesem aus dem Weg, wenn es darum geht zu für reichen, obwohl Sie müssen bedenken, dass for(auto&& ref: init) { /* body */ } zu etwa gleichwertig sein angegeben wird die folgenden (einige Details zu ignorieren, die nicht Sache hier zu tun):

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

Wir müssen uns jetzt fragen, was passiert, wenn *b ein xValue (dh der Iteratortyp hat ein operator*value_type&& Rückkehr, wie es der Fall mit std::move_iterator<Iterator> zB ist)? Es muss sich dann auf ein Objekt beziehen, dass ref überlebt, da die Zeile auto&& ref = *b; keine temporäre enthält. Daher ist es sicher. Andernfalls, wenn *b ein prvalue ist (d. H. Der Iteratortyp hat einen operator*, der T für einen Objekttyp T zurückgibt), wird die Lebensdauer des temporären Objekts für den Rest des Schleifenkörpers erweitert. In allen Fällen sind Sie sicher (der Fall, in dem *b ein Wert ist, der dem Leser als Übung überlassen wird).

Ich persönlich verwende stark auto&&, mit oder ohne Reichweite-für. Aber ich frage mich jedes Mal, ob der Initialisierer ein xvalue ist oder nicht, und wenn ja, was ist die Lebenszeit von dem, worauf Bezug genommen wird.

+0

Gute Info. Eine andere Perspektive ... das einzige temporäre Element, für das eine Funktion einen xvalue zurückgeben kann, ist ein rvalue-Argument. 'begin' und' end' nehmen keine Argumente an, daher ist die Weiterleitung ihrer Ergebnisse sicher. – Potatoswatter

+0

@Potatoswatter In diesem Fall ist es "* b", das von Interesse ist (und das ist in der Tat kein Argument, geschweige denn ein zeitweiliges Argument, wie Sie es richtig analysiert haben). 'begin' und' end' nehmen Argumente, aber sie funktionieren nur mit lvalues. –

+0

Ja, ich bezog mich auf die Mitglieder 'begin' und' end' * und schließt 'this' aus, weil es ein lvalue ist. 'operator *' nimmt ein Argument im Wesentlichen im selben Sinne wie 'begin' und' end'. Aber ja, das zählt wirklich. – Potatoswatter

Verwandte Themen