Decltype и оператор области видимости в С++

Мне нужно получить тип, который был предоставлен при создании экземпляра шаблона. Рассмотрим следующий пример:

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

Проблема с этим кодом использует оператор области видимости вместе с decltype. Visual С++ 2010 жалуется следующим образом:

ошибка C2039: "TUnderlying": не является членом "глобального пространства имен"

Я собрал некоторую информацию по теме в Википедии:

Комментируя официальный проект Комитета для С++ 0x, японский орган-член ISO отметил, что "оператор области видимости (::) не может применяться к decltype, но это должно быть. Было бы полезно в случае получить тип члена (вложенный тип) из экземпляра следующим образом:: [16]

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

Это и подобные проблемы были рассмотрены Дэвидом Вандевоордом и проголосовали за рабочий документ в марте 2010 года.

Итак, я считаю, что Visual С++ 2010 не реализовано. Я придумал это обходное решение:

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

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

Пропустил ли я какое-нибудь решение, более элегантное и менее подробное?

Ответы

Ответ 1

Это прозрачно заменяет ключевое слово decltype шаблоном на основе шаблона. Когда вам больше не нужно поддерживать MSVC2010, вы можете удалить определение макроса без изменения кода пользователя:

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

Это позволяет компилировать и работать с MSVC10:

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

Обратите внимание, что std::identity не является частью стандарта С++, но безопасно полагаться на него здесь, поскольку обходной путь ограничен компилятором, который включает std::identity в своей стандартной реализации библиотеки.

Ответ 2

Обходной путь выглядит относительно неплохо, но его нельзя расширять, а имена ужасны 1. Почему бы не использовать id?

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

И затем:

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

Неподтвержденный, но должен работать.


1 Как и в случае слишком многословного и даже описывающего его обходное решение, это может быть избыточным, а не полезной информацией в большинстве ситуаций.

Ответ 3

В качестве альтернативы вы можете легко вытащить тип с помощью вспомогательного шаблона шаблона:

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