2012-02-15 13 views
14

Necesito obtener el tipo que se proporcionó al crear una instancia de una plantilla. Considere el siguiente ejemplo:decltype y el operador de ámbito en C++

template <typename T> struct Foo 
{ 
    typedef T TUnderlying; 
}; 

static Foo<int> FooInt; 

class Bar 
{ 
public: 
    auto Automatic() -> decltype(FooInt)::TUnderlying 
    { 
    return decltype(FooInt)::TUnderlying(); 
    } 
}; 

int main() 
{ 
    Bar bar; 
    auto v = bar.Automatic(); 
    return 0; 
} 

El problema con este código es utilizar el operador de ámbito junto con decltype. Visual C++ 2010 se queja así:

error C2039: 'TUnderlying': no ​​es un miembro de '`espacio de nombres global''

Reuní algo de información sobre el tema en la Wikipedia:

Al comentar el borrador del Comité formal para C++ 0x, el miembro miembro de ISO japonés señaló que "un operador de ámbito (:) no se puede aplicar a decltype, pero debería serlo. Sería útil en el caso obtener el tipo de miembro (anidado) tipo) de una instancia como sigue ": [16]

vector<int> v; 
decltype(v)::value_type i = 0; // int i = 0; 

Esto y otros problemas similares fueron abordados por David Vandevoorde, y votaron en el documento de trabajo en marzo de 2010.

Así que creo que el Visual C++ 2010 no tiene esto implementado. Se me ocurrió esta solución alternativa:

template <typename T> struct ScopeOperatorWorkaroundWrapper 
{ 
    typedef typename T::TUnderlying TTypedeffedUnderlying; 
}; 

auto Automatic() -> ScopeOperatorWorkaroundWrapper<decltype(FooInt)>::TTypedeffedUnderlying 
{ 
    return ScopeOperatorWorkaroundWrapper<decltype(FooInt)>::TTypedeffedUnderlying(); 
} 

¿Extraño alguna solución que sea más elegante y menos detallada?

+1

¿Has probado 'FooInt :: TUnderlying' en lugar de' decltype (FooInt) :: TUnderlying'?No veo lo que espera obtener a través de 'decltype' aquí. – sellibitze

+0

Todo lo que agrega es 'ScopeOperatorWorkaroundWrapper <>', no sé cuánto "menos detallado" podría quererlo. Después de todo, es una solución. – PlasmaHH

Respuesta

12

Esto reemplaza de forma transparente la palabra clave decltype con la solución basada en la plantilla. Una vez que ya no es necesario para apoyar MSVC2010 puede quitar la definición de la macro sin cambiar ningún código de usuario:

#if _MSC_VER == 1600 
#include <utility> 
#define decltype(...) \ 
    std::identity<decltype(__VA_ARGS__)>::type 
#endif 

que permite a este compilar y trabajar en MSVC10:

std::vector<int> v; 
decltype(v)::value_type i = 0; 

Tenga en cuenta que std::identity no es parte del estándar de C++, pero es seguro confiar aquí ya que la solución está limitada a un compilador que incluye std::identity en su implementación de biblioteca estándar.

+0

Utilice macros variadic para resolver el problema de la coma. '#define decltype (...) detail :: type_helper type'. Tenga en cuenta que 'decltype (x)' puede no ser lo mismo que 'decltype ((x))', por lo que su 't2' podría dar como resultado un tipo diferente. – kennytm

+0

@KennyTM: Gracias, lo he incorporado a la respuesta. –

+0

Veo esta solución como la menos intrusiva y la más fácil de eliminar una vez que el compilador implementa esta característica. ¡Gracias por tu tiempo! –

2

La solución se ve relativamente bien, pero no es extensible y los nombres son horribles . ¿Por qué no usar id?

template <typename T> 
struct id { 
    typedef T type; 
}; 

Y luego:

id<decltype(FooInt)>::type::TUnderlying; 

no probado, pero debería funcionar.


igual que en, demasiado prolijo y aunque ellos describen que es una solución, esto puede ser redundante y no una información útil en la mayoría de las situaciones.

+0

Gracias por su contribución. En cuanto a los nombres, fue solo un ejemplo y admito que los nombres podrían ser un poco más cortos, pero prefiero nombres que capturen el razonamiento del problema. La "id" simple sería más tarde (cuando me olvidé de ella) no me digas nada de que se trataba simplemente de un hack temporal. Creo que no es necesario contar con funciones modernas de autocompletado para hacer que mi código sea difícil de entender con abreviaturas crípticas. –

+0

@Milan 'id' es el nombre correcto para esta metafunción (es la [función de identidad] (http://en.wikipedia.org/wiki/Identity_function)), y creo que ya está definida en algún lugar en C++ 11 precisamente para servir como una solución sintáctica en lugares donde no se puede usar un tipo simple. De cualquier manera, * debes * usar este nombre ya que es el nombre establecido para este concepto y generalmente se entiende. De ninguna manera es una "abreviación críptica" (que acepto nunca debería usarse). –

+0

@Milan Pero como mencionaste autocompletar: la razón para evitar los nombres largos es * no * que lleva mucho tiempo escribirlos (la finalización automática ayuda aquí, es cierto). Pero también hace que el código * sea ilegible *, y los nombres concisos (breves, pero precisos) siempre deben ser preferidos. –

0

Como alternativa, se puede tirar fácilmente el tipo a cabo utilizando una plantilla de función auxiliar:

template <typename T> struct Foo 
{ 
    typedef T TUnderlying; 
}; 

static Foo<int> FooInt; 

template <typename T> 
typename Foo<T>::TUnderlying foo_underlying(Foo<T> const &) 
{ 
    return typename Foo<T>::TUnderlying(); 
} 

class Bar 
{ 
public: 
// auto Automatic() -> decltype(FooInt)::Underlying 
// { 
//  return decltype(FooInt)::Underlying; 
// } 
    auto Automatic() -> decltype(foo_underlying(FooInt)) 
    { 
     return foo_underlying(FooInt); 
    } 
}; 

int main() 
{ 
    Bar bar; 
    auto v = bar.Automatic(); 
}