Eliminar referencia en decltype (devuelve T en lugar de T & donde T & es el decltype)


(Si eres un pro de C++11, salta al párrafo en negrita.)

Digamos que quiero escribir un método de plantilla que llama y devuelve el resultado de un objeto pasado cuyo tipo es el parámetro de plantilla:

template<ReturnType, T>
ReturnType doSomething(const T & foo) {
    return foo.bar(); // EDIT: Might also be an expression introducing a temp val
}

Así que T tiene que tener un método ReturnType T::bar() const para ser utilizado en una llamada como esta:

struct MyClass {
    ...
    int bar() const;
    ...
};
...
MyClass object;
int x = doSomething<int, MyClass>(object);

No tenemos que escribir MyClass gracias a la deducción de tipo y la llamada se convierte en:

int x = doSomething<int>(object);

Pero omitir <int> también resulta en un error de compilación porque el método no requiere devolver int para ser asignado a x después (podría devolver char por ejemplo).

En C++0x / 11 tenemos los auto y decltype con los que podemos deducir el tipo de retorno de un método de plantilla:

template<T>
auto doSomething(const T & foo) -> decltype(foo.bar()) {
    return foo.bar(); // EDIT: Might also be an expression introducing a temp val
}

El compilador ahora descubrirá cuál es el tipo de foo.bar() y solo lo usa como el tipo de retorno. Con nuestra clase concreta MyClass esto será un int y lo siguiente sería suficiente:

int x = doSomething(object);

Ahora a mi pregunta:

Si MyClass define bar() como devolver un int&, el tipo de retorno de doSomething(object) también será un int& = decltype(foo.bar()). Esto es un problema, ya que como G++ ahora cumple que estoy devolviendo la referencia a temporal.

¿Cómo puedo arreglar esto? ¿Hay algo como remove_reference que se pueda usar como remove_reference(decltype(foo.bar()))?

Pensé en simplemente declarar un método helper que toma un T& y devuelve un T y luego definir el tipo de retorno de doSomething como decltype(helper(foo.bar())). Pero tiene que haber una mejor manera, lo estoy sintiendo.

Author: leemes, 2012-11-03

1 answers

Para eliminar una referencia:

#include <type_traits>

static_assert(std::is_same<int, std::remove_reference<int&>::type>::value, "wat");

En su caso:

template <typename T>
auto doSomething(const T& foo)
    -> typename std::remove_reference<decltype(foo.bar())>::type
{
    return foo.bar();
}

Solo para ser claro, tenga en cuenta que como está escrito devolver una referencia está bien:

#include <type_traits>

struct f
{
    int& bar() const
    {
        static int i = 0;
        return i;
    } 
};

template <typename T>
auto doSomething(const T& foo)
    -> decltype(foo.bar())
{ 
    return foo.bar();
}

int main()
{
    f x;
    return doSomething(x);
}

La referencia devuelta puede simplemente pasarse sin error. Su ejemplo en el comentario es donde se vuelve importante y útil:

template <typename T>
auto doSomething(const T& foo)
    -> decltype(foo.bar())
{ 
    return foo.bar() + 1; // oops
}
 48
Author: GManNickG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-02 20:21:57