2011-11-27 12 views
7

Pregunta simple, ¿por qué no funciona the following (lo que implica una copia de ci)?¿Por qué `const int ci = 2; std :: forward <int> (ci); `trabajo y cómo solucionarlo/solucionarlo?

#include <utility> 

int main(){ 
    const int ci = 2; 
    std::forward<int>(ci); 
} 

El problema se manifestó al escribir algunas cosas plantilla, en la que tengo un tipo simple titular de la siguiente manera. Para evitar copias innecesarias, utilizo el reenvío perfecto donde sea posible, pero parece ser la raíz del problema.

template<class T> 
struct holder{ 
    T value; 

    holder(T&& val) 
     : value(std::forward<T>(val)) 
    {} 
}; 

template<class T> 
holder<T> hold(T&& val){ 
    // T will be deduced as int, because literal `5` is a prvalue 
    // which can be bound to `int&&` 
    return holder<T>(std::forward<T>(val)); 
} 

template<class T> 
void foo(holder<T> const& h) 
{ 
    std::tuple<T> t; // contrived, actual function takes more parameters 
    std::get<0>(t) = std::forward<T>(h.value); // h.value is `const T` 
} 

int main(){ 
    foo(hold(5)); 
} 

Si necesita más información, hágamelo saber.
Cualquier idea para eludir este problema es muy apreciada.

+0

Debe hacer esto: 'template struct holder {holder (T val): value (std :: move (val)) {}}; plantilla titular :: type> asimiento (T val) {return titular (std :: movimiento (val)); } '(Sí, soy consciente de que se ve abrumadoramente. :)) – GManNickG

+0

@GMan: Pero no quiero eliminar la referencia. :( – Xeo

+1

Entonces llaman como 'hold (std :: Ref (x));': P – GManNickG

Respuesta

15

Este:

#include <utility> 

int main(){ 
    const int ci = 2; 
    std::forward<int>(ci); 
} 

no funciona porque no se puede emitir de forma implícita distancia const. std::forward<T>(u) debe leerse como:

Forward u as a T .

Está intentando decir:

Forward an lvalue `const int` as an rvalue `int`. 

la que tira a la basura el const. Para evitar tirar el const usted podría:

#include <utility> 

int main(){ 
    const int ci = 2; 
    std::forward<const int>(ci); 
} 

que dice:

Forward an lvalue `const int` as an rvalue `const int`. 

En su código:

template<class T> 
void foo(holder<T> const& h) 
{ 
    std::tuple<T> t; // contrived, actual function takes more parameters 
    std::get<0>(t) = std::forward<T>(h.value); // h.value is `const T` 
} 

la const calificador en h afecta a la expresión de selección de los miembros de datos h.value. h.value es un const lvalue int. Puede usar forward para cambiarlo en const rvalue int, o puede usar forward para pasarlo sin cambios (como const lvalue int). Usted podría incluso use forward para agregar volatile (aunque no puedo pensar en una buena razón para hacerlo).

En su ejemplo, estoy viendo ninguna razón para utilizar forward en absoluto (a menos que tome la const fuera de h).

std::get<0>(t) = h.value; // h.value is `const T` 

Su comentario es aún correcto todavía.

Es una lectura en seco, pero N2951 encuestas lo que puede y no puede hacer con forward y por qué. Esto fue modificado por N3143 justo antes de la estandarización, pero los casos de uso y la justificación de siguen siendo válidos y sin cambios en la formulación final de N3143.

cosas que puede hacer con forward:

  • puede reenviar un valor-I como un valor izquierdo.
  • Puede reenviar un lvalue como un valor r.
  • Puede reenviar un valor rvalue.
  • Puede reenviar expresiones menos cv-qualified a más expresiones cv-qualified.
  • Puede reenviar las expresiones del tipo derivado a un tipo de base accesible, no ambigua.

Cosas que se pueden hacer no con forward:

  • No se puede reenviar un valor de lado derecho como un valor izquierdo.
  • No puede reenviar más expresiones cv-qualified a menos expresiones cv-qualified.
  • Usted no puede reenviar conversiones de tipos arbitrarios (por ejemplo, hacia delante una int como double).
+0

Gracias por esta extensa respuesta. ¿Hay alguna forma de "guardar" la entrega de un valor? Como dije en mi comentario sobre la pregunta en sí, estoy tratando de implementar parámetros posicionales, y me gustaría reenviar todos los parámetros tal como se pasaron directamente a la función. [Aquí] (http://ideone.com/jeprD) es el código completo que tengo actualmente (~ 300 LoC), aunque a GCC no parece gustarle mi truco de SFINAE en 'switch_'. – Xeo

+0

Ya encontré algunas limitaciones en mi sistema, como que no puedo enlazar las temporarias de tipo 'T' a' T const & 'o' T && 'en' param_pack' sin obtener referencias colgantes. ¿Hay alguna manera de corregir/mejorar eso (si estás dispuesto a pasar por el código)? – Xeo

+0

@Xeo: No estoy seguro. Pero pude arreglar tu código de modo que se ejecute en clang/libC++. 'std :: move (pos :: template switch_ <0> (arg1, arg2, arg3) .value' (agrega" plantilla "3 lugares).Hacer param_pack BoundList constructor menos genérico: 'plantilla param_pack (lista_encuaderna && bl)'. Con estos cambios el código se ejecuta para mí y salidas: 'Valor ctor Mover ctor Mover ctor Mover ctor Copiar ctor Mover ctor Mover ctor hi 3 1 Dtor Dtor' –

Cuestiones relacionadas