2011-11-25 16 views
6

Tengo algunas dificultades con el reenvío perfecto.Reenvío perfecto y std :: tuple (u otra clase con plantilla)

Aquí es mi nivel actual de entendimiento: Plantilla de pegamento + referencia rvalue + std :: hacia adelante y un modo mágico especial en donde se activan reglas de deducción plantilla no tienen el mismo significado como de costumbre, pero se hacen a mano para permitir el reenvío perfecto. Ejemplo:

template <typename T> 
void outer(T&& t) 
{ 
    inner(std::forward<T>(t)); // perfect forwarding activated 
} 

Pero, ¿qué ocurre si T es en realidad una clase de plantilla? Por ejemplo, ¿cómo puedo perfeccionar el reenvío de una std :: tuple? Si utilizo un T & & como aboce perderé toda la información de tipo de los objetos contenidos en la tupla. Sin embargo
el siguiente código no puede trabajar:

template <typename... Args> 
void outer(std::tuple<Args...>&& t) 
{ 
    inner(std::forward<std::tuple<Args...>>(t)); 
    use_args_types_for_something_else<Args...>(); // I need to have Args available 
} 

int main() 
{ 
    std::tuple<int, double, float> t(4, 5.0, 4.0f); 
    outer(t); 
} 

última instantánea gcc dice:

error: cannot bind 'std::tuple<int, double, float> lvalue to 
std::tuple<int, double, float>&& 

Así que, claramente, estamos todavía en el caso general, no molde donde lvalue No se puede enlazar para validar la referencia. "Modo forwading perfecto" no se activa

así que traté de ser astuto y pasar mi tupla como plantilla plantilla:

template < 
    typename... Args 
    template <typename...> class T 
> 
void outer(T<Args...>&& t) 
{ 
    inner(std::forward<T<Args...>>(t)); 
    use_args_type_for_something_else<Args...>(); 
} 

Pero sigo teniendo el mismo error.

+0

¿No puede simplemente llamar a std :: forward sin especificar el tipo (ya que es una función de plantilla y puede usar la deducción)? 'std :: forward (t)' – SoapBox

Respuesta

3

reenvío perfecto sólo funciona si el tipo del parámetro es un tipo de plantilla para la función, por lo que la única manera de lograr el reenvío perfecto es igual que en el primer ejemplo:

template <typename T> 
void outer(T&& t) 
{ 
    inner(std::forward<T>(t)); // perfect forwarding activated 
} 

Los trabajos anteriores, ya que es un caso especial donde T se deduce como SomeType& o SomeType&&.

Esto, sin embargo, no significa que la información de tipo para los elementos de tupla se pierde para siempre. Todavía es recuperable (aunque no creo que puedas escribir un paquete de plantilla variadic). Por ejemplo, todavía se puede llamar use_args_types_for_something_else así:

template <class T> 
struct call_uses_args; 

template <class ...Args> 
struct call_uses_args<std::tuple<Args...>> 
{ 
    void call() const { use_args_types_for_something_else<Args...>(); } 
}; 

template <typename TupleT> 
void outer(TupleT&& t) 
{ 
    inner(std::forward<TupleT>(t)); 
    call_uses_args<typename std::remove_reference<TupleT>::type>().call(); 
} 

Puede haber ninguna buena solución general, sin embargo, pero esperemos que este tipo de situaciones son raras. (Por ejemplo, en este ejemplo en particular, podría ser más simple simplemente sobrecargar outer.)

+0

Gracias por su respuesta agradable y precisa. Por lo tanto, con respecto al reenvío perfecto en C++ 11, todavía me siento un poco molesto porque confío tanto en este extraño combo "template + rref + std :: forward". Crea un pequeño punto donde las reglas generales (como "referencias rvalue SÓLO se unen a rvalue") ya no se aplican. Me pregunto si alguna sintaxis especial para el reenvío perfecto no hubiera sido mejor para C++ 11. De todos modos, es cierto que una solución en este caso no es tan importante, ya que aún es posible hacer dos sobrecargas para 'outer', una con const ref y otra con rref para emular PF. –

Cuestiones relacionadas