const auto&
Sería suficiente si quiero realizar operaciones de solo lectura. Sin embargo, me he encontrado
for (auto&& e : v) // v is non-const
un par de veces recientemente. Esto me hace preguntarme:
¿Es posible que en algunos casos poco conocidos exista algún beneficio de rendimiento al usar referencias de reenvío, en comparación con auto&
oconst auto&
?
( shared_ptr
es sospechoso de casos oscuros de esquina)
Actualización Dos ejemplos que encontré en mis favoritos:
¿Alguna desventaja de usar la referencia constante al iterar sobre tipos básicos?
¿Puedo iterar fácilmente sobre los valores de un mapa usando un bucle for basado en rango?
Concéntrese en la pregunta: ¿por qué querría usar auto && en bucles for basados en rango?
auto&&
en bucles for basados en rangos?Respuestas:
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 constante. Por ejemplo, considere:
Esto no se compila porque rvalue
vector<bool>::reference
devuelto poriterator
no se vinculará a una referencia lvalue no constante. Pero esto funcionará:Dicho todo esto, no codificaría de esta manera a menos que supieras que necesitas satisfacer tal caso de uso. Es decir, no haría esto gratuitamente porque hace que la gente se pregunte qué estás haciendo. Y si lo hiciera, no estaría de más incluir un comentario sobre por qué:
Editar
Este último caso mío debería ser una plantilla para que tenga sentido. Si sabe que el bucle siempre está manejando una referencia de proxy, entonces
auto
funcionaría tan bien comoauto&&
. Pero cuando el bucle a veces manejaba referencias no proxy y, a veces, referencias proxy, entonces creo queauto&&
se convertiría en la solución de elección.fuente
const auto&
cuando quiero que el compilador me ayude a comprobar que no modifico accidentalmente los elementos de la secuencia.auto&&
en código genérico donde necesito modificar los elementos de la secuencia. Si no lo hago, me quedaréauto const&
.El uso de referencias universales
auto&&
o con un bucle basado en rango tiene la ventaja de que captura lo que obtiene. Para la mayoría de los tipos de iteradores, probablemente obtendrá una o una para algún tipo . El caso interesante es donde la desreferenciación de un iterador produce un temporal: C ++ 2011 tiene requisitos relajados y los iteradores no son necesariamente necesarios para producir un valor l. El uso de referencias universales coincide con el reenvío de argumentos en :for
T&
T const&
T
std::for_each()
El objeto de función
f
puede tratarT&
,T const&
yT
de manera diferente. ¿Por qué deberíafor
ser diferente el cuerpo de un bucle basado en rango? Por supuesto, para aprovechar realmente haber deducido el tipo usando referencias universales, necesitaría transmitirlas de manera correspondiente:Por supuesto, usar
std::forward()
significa que acepta cualquier valor devuelto desde el que se moverá. Si objetos como este tienen mucho sentido en el código que no es de plantilla, no lo sé (¿todavía?). Puedo imaginar que el uso de referencias universales puede ofrecer más información al compilador para hacer lo correcto. En el código con plantilla, se mantiene al margen de tomar decisiones sobre lo que debería suceder con los objetos.fuente
Prácticamente siempre uso
auto&&
. ¿Por qué ser mordido por un caso extremo cuando no es necesario? También es más corto de escribir, y simplemente lo encuentro más ... transparente. Cuando lo usaauto&& x
, entonces sabe quex
es exactamente*it
, siempre.fuente
const
conauto&&
si esconst auto&
suficiente. La pregunta se refiere a los casos de esquina en los que me pueden picar. ¿Cuáles son los casos de esquina que aún no han sido mencionados por Dietmar o Howard?auto&&
. Si el tipo que está capturando debe moverse en el cuerpo del bucle (por ejemplo) y se cambia a una fecha posterior para que se resuelva en unconst&
tipo, su código seguirá funcionando silenciosamente, pero sus movimientos se convertirán en copias. Este código será muy engañoso. Sin embargo, si especifica explícitamente el tipo como una referencia de valor r, quien haya cambiado el tipo de contenedor obtendrá un error de compilación porque realmente desea que estos objetos se muevan y no se copien ...for (std::decay_t<decltype(*v.begin())>&& e: v)
? Supongo que hay una mejor manera ...auto&&
dará una "referencia universal", por lo que cualquier otra cosa le dará un tipo más específico. Siv
se asume que es unvector
, podría hacerlodecltype(v)::value_type&&
, que es lo que supongo que deseaba tomando el resultado deoperator*
en un tipo de iterador. También puede hacerdecltype(begin(v))::value_type&&
para verificar los tipos del iterador en lugar del contenedor. Sin embargo, si tenemos tan poca información sobre el tipo, podríamos considerar que es un poco más claro ir conauto&&
...