Estilo apropriado para declaração baseada em intervalos para

Esta questão mencionou o uso idiomático e idiomático do C ++ 11 baseado em intervalo para.

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

Eu tenho tido dúvidas sobre o tipo de referência que você deveria usar, no entanto. Iteradores de input podem retornar valores. Embora o tipo implícito introduzido por auto possa ser deduzido a const que se liga a um rvalue, isso parece não ocorrer.

A melhor prática geral é usar o encaminhamento perfeito?

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

Não vejo desvantagem aqui, mas parece um pouco fofo demais. Talvez eu ainda não tenha escrito o suficiente C ++ 11.

Primeiro, alguns conselhos gerais sobre como usar o auto que não é específico para intervalo. auto&& pode ser problemático se o inicializador for um xvalue referente a um temporário, uma vez que a extensão da vida útil pode não ser aplicada neste caso. Para simplificar e com código:

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

A grande pista de que há algo obscuro acontecendo é que o id tem o tipo de retorno T&& . Se ele retornasse T então id(whatever) que id(whatever) seria um prvalue, e o temporário retornado teria seu tempo de vida estendido (no entanto, isso envolveria uma construção).


Com isso fora do caminho, quando se trata de range-for, você tem que lembrar que for(auto&& ref: init) { /* body */ } é especificado para ser mais ou menos equivalente ao seguinte (ignorando alguns detalhes que don ‘ Não importa aqui):

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

Precisamos nos perguntar agora, e se *b é um xvalue (ou seja, o tipo iterador tem um operator* retornando value_type&& , como é o caso, por exemplo, com std::move_iterator )? Deve então referir-se a um object que irá sobreviver à ref , já que a linha auto&& ref = *b; não envolve temporário. Por isso é seguro. Caso contrário, se *b é um prvalue (ou seja, o tipo iterador tem um operator* retornando T para algum tipo de object T ), então o tempo de vida do temporário é estendido para o resto do corpo do loop. Em todos os casos, você está seguro (o caso em que *b é um lvalue sendo deixado como um exercício para o leitor).

Eu pessoalmente faço uso pesado de auto&& , com ou sem intervalo para. Mas eu sempre me pergunto se o inicializador é um xvalue ou não, e se é, qual é o tempo de vida do que está sendo referido.

    Intereting Posts