decltype and the scope operator in C++

2019-01-15 12:46发布

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?

3条回答
倾城 Initia
2楼-- · 2019-01-15 13:09

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.

查看更多
仙女界的扛把子
3楼-- · 2019-01-15 13:16

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.

查看更多
倾城 Initia
4楼-- · 2019-01-15 13:22

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();
}
查看更多
登录 后发表回答