2012-02-15 14 views
14

Ho bisogno di ottenere il tipo che è stato fornito durante l'istanziazione di un modello. Considerare il seguente esempio:decltype e l'operatore scope in 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; 
} 

Il problema con questo codice utilizza l'operatore di ambito insieme a decltype. Visual C++ 2010 si lamenta così:

errore C2039: 'TUnderlying': non è un membro di '`namespace globale''

ho raccolto alcune informazioni sul tema su Wikipedia:

Commentando il Draft di commissione formale per C++ 0x, il membro del membro ISO giapponese ha osservato che "un operatore di ambito (: :) non può essere applicato a decltype, ma dovrebbe essere. Sarebbe utile nel caso di ottenere il tipo di membro (nidificato- tipo) da un'istanza come segue ": [16]

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

Questo e altri problemi simili sono stati affrontati da David Vandevoorde e votati nel documento di lavoro nel marzo 2010.

Quindi penso che il Visual C++ 2010 non lo abbia implementato. Mi è venuta in mente questa soluzione:

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

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

Ho perso una soluzione più elegante e meno dettagliata?

+1

Hai provato 'FooInt :: TUnderlying' invece di' decltype (FooInt) :: TUnderlying'?Non vedo cosa ti aspetti di guadagnare con 'decltype' qui. – sellibitze

+0

Tutto quello che aggiungi è 'ScopeOperatorWorkaroundWrapper <>', non so quanto "meno dettagliato" lo si possa desiderare. Dopo tutto, è una soluzione. – PlasmaHH

risposta

12

Sostituisce in modo trasparente la parola chiave decltype con la soluzione alternativa basata sul modello. Una volta che non è più necessario sostenere MSVC2010 è possibile rimuovere la definizione della macro senza modificare il codice utente:

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

che permette questo per compilare e lavorare su MSVC10:

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

noti che std::identity non fa parte dello standard C++, ma è sicuro affidarsi qui in quanto la soluzione è limitata a un compilatore che include std::identity nella sua implementazione di libreria standard.

+0

Utilizzare macro variadic per risolvere il problema della virgola. '#define decltype (...) detail :: type_helper :: type'. Notate che 'decltype (x)' potrebbe non essere lo stesso di 'decltype ((x))', quindi il vostro 't2' potrebbe dare come risultato un tipo diverso. – kennytm

+0

@KennyTM: Grazie, l'ho incorporato nella risposta. –

+0

Vedo questa soluzione come la meno intrusiva e più facile da rimuovere una volta che il compilatore implementa questa funzione. Grazie per il tuo tempo! –

2

La soluzione alternativa è relativamente buona ma non è estensibile ei nomi sono orribili . Perché non utilizzare id?

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

E poi:

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

testato, ma dovrebbe funzionare.


Come in, troppo prolisso e benché descrivono che è una soluzione, questo può essere ridondante e non un'informazione utile nella maggior parte delle situazioni.

+0

Grazie per l'input. Per quanto riguarda i nomi - era solo un esempio e ammetto che i nomi potrebbero essere un po 'più brevi, ma preferisco i nomi che catturano il ragionamento del problema. Plain "id" sarebbe più tardi (quando me ne dimenticherò) non dirmi nulla che fosse solo un hack temporaneo. Penso che non sia necessario con le moderne funzionalità di completamento automatico a portata di mano per rendere il mio codice difficile da capire con abbreviazioni criptici. –

+0

@Milan 'id' è il nome corretto per questa metafunzione (è la [funzione identità] (http://en.wikipedia.org/wiki/Identity_function)), e penso che sia effettivamente già definito da qualche parte in C++ 11 precisamente per fungere da soluzione sintattica in luoghi in cui un tipo normale non può essere utilizzato. In entrambi i casi, * dovresti * usare questo nome poiché è il nome stabilito per questo concetto ed è generalmente compreso. Non è affatto una "abbreviazione criptica" (che sono d'accordo non dovrebbe mai essere usato). –

+0

@Milan Ma visto che hai citato il completamento automatico: la ragione per evitare nomi lunghi è * non * che impiega molto tempo a digitarli (il completamento automatico aiuta qui, vero). Ma rende anche il codice * illeggibile * e i nomi concisi (brevi ma precisi) dovrebbero sempre essere preferiti. –

0

In alternativa, si può facilmente estrarre il tipo utilizzando un modello di funzione di supporto:

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(); 
}