Milan Stezka
Milan Stezka

Reputation: 162

decltype and the scope operator in C++

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

Answers (3)

JoeG
JoeG

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

Useless
Useless

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

Konrad Rudolph
Konrad Rudolph

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

Related Questions