2012-02-15 3 views
14

Мне нужно получить тип, который был предоставлен при создании экземпляра шаблона. Рассмотрим следующий пример:decltype и оператор области видимости в 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; 
} 

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

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

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

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

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

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

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

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

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

Я пропустил любое более элегантное и менее подробное решение?

+1

вы пробовали 'FooInt :: TUnderlying' вместо' decltype (FooInt) :: TUnderlying'?Я не вижу, что вы ожидаете получить через 'decltype' здесь. – sellibitze

+0

Все, что вы добавляете, это 'ScopeOperatorWorkaroundWrapper <>', я не знаю, сколько «менее подробных» вы могли бы захотеть. В конце концов, это обходной путь. – PlasmaHH

ответ

12

Это прозрачно заменяет ключевое слово 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 не является частью стандарта C++, но безопасно полагаться на него здесь, поскольку обходной путь ограничивается компилятором, который включает в себя стандартную библиотечную реализацию std::identity.

+0

Используйте переменные макросы для решения проблемы с запятой. '#define decltype (...) detail :: type_helper :: type'. Обратите внимание, что 'decltype (x)' может не совпадать с 'decltype ((x))', поэтому ваш 't2' может привести к другому типу. – kennytm

+0

@KennyTM: Спасибо, я включил это в ответ. –

+0

Я рассматриваю это решение как наименее навязчивое и проще всего удалить, когда компилятор реализует эту функцию. Спасибо за ваше время! –

2

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

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

И потом:

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

проверялось, но должно работать.


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

+0

Благодарим вас за ввод. Что касается имен - это был всего лишь пример, и я признаю, что имена могут быть немного короче, но я предпочитаю имена, которые фиксируют аргументацию проблемы. Обычный «id» позже (когда я забуду об этом) ничего не скажу, что это был просто временный хак. Я думаю, что нет необходимости в современных функциях автоматического завершения, чтобы сделать мой код трудным для понимания с помощью загадочных сокращений. –

+0

@Milan 'id' - правильное имя для этого метафунта (это функция [identity function] (http://en.wikipedia.org/wiki/Identity_function)), и я думаю, что она на самом деле уже определена где-то на C++ 11 точно для того, чтобы служить синтаксическим обходным решением в местах, где простой тип не может быть использован. В любом случае, вы * должны * использовать это имя, поскольку оно является установленным именем для этой концепции, и это обычно понимается. Это отнюдь не «загадочная аббревиатура» (которую я согласен никогда не использовать). –

+0

@Milan Но так как вы упомянули автоматическое завершение: причина избежать длинных имен - это * не *, что для их ввода требуется много времени (автоматическое завершение помогает здесь, правда). Но он также делает код * нечитаемым *, и краткие, но точные имена всегда должны быть предпочтительными. –

0

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

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