dev compiler c++ performance for-loop c++11 move-semantics

compiler - c++14



¿Cuál es la ventaja de usar referencias universales en bucles basados en rangos? (3)

const auto& sería suficiente si quiero realizar operaciones de solo lectura. Sin embargo, me he topado con

for (auto&& e : v) // v is non-const

un par de veces recientemente. Esto me hace pensar:

¿Es posible que en algunos casos oscuros de esquina haya algún beneficio de rendimiento en el uso de referencias universales, en comparación con el auto& const auto& ?

( shared_ptr es un sospechoso para casos de esquina oscuros)

Actualizar dos ejemplos que encontré en mis favoritos:

¿Alguna desventaja de usar referencia constante cuando se itera sobre tipos básicos?
¿Puedo iterar fácilmente sobre los valores de un mapa usando un bucle for basado en rango?

Por favor, concéntrese en la pregunta: ¿por qué querría usar auto && en el rango para bucles?


El uso de auto&& o referencias universales con un rango basado for -loop tiene la ventaja de que usted captura lo que obtiene. Para la mayoría de los tipos de iteradores probablemente obtendrás un T& un T const& para algún tipo T El caso interesante es cuando la desreferenciación de un iterador produce un efecto temporal: C ++ 2011 tiene requisitos relajados y los iteradores no están necesariamente obligados a generar un valor l. El uso de referencias universales coincide con el reenvío de argumentos en std::for_each() :

template <typename InIt, typename F> F std::for_each(InIt it, InIt end, F f) { for (; it != end; ++it) { f(*it); // <---------------------- here } return f; }

El objeto de función f puede tratar T& , T const& , y T diferente. ¿Por qué el cuerpo de un bucle basado for rango debería ser diferente? Por supuesto, para aprovechar el hecho de haber deducido el tipo utilizando referencias universales, necesitaría pasarlas de manera correspondiente:

for (auto&& x: range) { f(std::forward<decltype(x)>(x)); }

Por supuesto, el uso de std::forward() significa que acepta cualquier valor devuelto desde el que se moverá. Si los objetos como este tienen mucho sentido en un código que no sea de plantilla, no lo sé (¿todavía?). Me imagino que el uso de referencias universales puede ofrecer más información al compilador para hacer lo correcto. En el código de plantilla, queda fuera de tomar una decisión sobre lo que debería suceder con los objetos.


La única ventaja que puedo ver es cuando el iterador de secuencia devuelve una referencia de proxy y necesita operar en esa referencia de una manera no const. Por ejemplo, considere:

#include <vector> int main() { std::vector<bool> v(10); for (auto& e : v) e = true; }

Esto no se compila porque la referencia de vector<bool>::reference rvalue vector<bool>::reference devuelta por el iterator no se vinculará a una referencia de valor l no const. Pero esto funcionará:

#include <vector> int main() { std::vector<bool> v(10); for (auto&& e : v) e = true; }

Dicho todo esto, no codificaría de esta manera a menos que supiera que necesitaba satisfacer ese caso de uso. Es decir, no lo haría gratuitamente porque hace que las personas se pregunten qué estás tramando. Y si lo hiciera, no estaría de más incluir comentarios sobre por qué:

#include <vector> int main() { std::vector<bool> v(10); // using auto&& so that I can handle the rvalue reference // returned for the vector<bool> case for (auto&& e : v) e = true; }

Editar

Este último caso mío debería ser realmente una plantilla para tener sentido. Si sabes que el ciclo siempre está manejando una referencia de proxy, entonces auto funcionará tan bien como auto&& . Pero cuando el bucle a veces manejaba referencias no proxy y, a veces, referencias de proxy, entonces creo que auto&& se convertiría en la solución de elección.


Yo casi siempre uso auto&& . ¿Por qué ser mordido por una caja de borde cuando no es necesario? También es más corto escribir, y simplemente lo encuentro más ... transparente. Cuando usa auto&& x , entonces sabe que x es exactamente *it , cada vez.