Reputation: 162
I need to obtain the type which was supplied when instantiating a template. Consider the following example:
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;
}
Problem with this code is using the scope operator together with decltype. Visual C++ 2010 complains like this:
error C2039: 'TUnderlying' : is not a member of '`global namespace''
I gathered some information on the topic on Wikipedia:
While commenting on the formal Committee Draft for C++0x, the Japanese ISO member body noted that "a scope operator(::) cannot be applied to decltype, but it should be. It would be useful in the case to obtain member type(nested-type) from an instance as follows":[16]
vector<int> v;
decltype(v)::value_type i = 0; // int i = 0;
This, and similar issues were addressed by David Vandevoorde, and voted into the working paper in March 2010.
So I reckon the Visual C++ 2010 does not have this implemented. I came up with this workaround:
template <typename T> struct ScopeOperatorWorkaroundWrapper
{
typedef typename T::TUnderlying TTypedeffedUnderlying;
};
auto Automatic() -> ScopeOperatorWorkaroundWrapper<decltype(FooInt)>::TTypedeffedUnderlying
{
return ScopeOperatorWorkaroundWrapper<decltype(FooInt)>::TTypedeffedUnderlying();
}
Did I miss any solution which is more elegant and less verbose?
Upvotes: 15
Views: 2439
Reputation: 13192
This transparently replaces the decltype
keyword with the template based workaround. Once you no longer need to support MSVC2010 you can remove the macro definition without changing any user code:
#if _MSC_VER == 1600
#include <utility>
#define decltype(...) \
std::identity<decltype(__VA_ARGS__)>::type
#endif
Which allows this to compile and work on MSVC10:
std::vector<int> v;
decltype(v)::value_type i = 0;
Note that std::identity
isn't part of the C++ standard, but it's safe to rely on it here as the workaround is limited to a compiler which includes std::identity
in its standard library implementation.
Upvotes: 12
Reputation: 67792
As an alternative, you can easily pull the type out using a function template helper:
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();
}
Upvotes: 0
Reputation: 545923
The workaround looks relatively fine but it’s not extensible and the names are horrible1. Why not use id
?
template <typename T>
struct id {
typedef T type;
};
And then:
id<decltype(FooInt)>::type::TUnderlying;
Untested, but should work.
1 As in, too verbose and even though they describe that it’s a workaround, this may be redundant and not a useful information in most of the situations.
Upvotes: 2