Получив определение типа шаблона параметра, или если это невозможно, становится тип самого
Я пытаюсь получить тип, на который указывает typedef, из класса (ValueType
typedef), который я определил. Однако, когда это не удается, я хочу, чтобы он вернул заданный тип (например, если я дам ему double
, я хочу, чтобы он вернул double
). Вот что у меня есть до сих пор:
struct myClass { typedef double ValueType; };
template < typename V, typename U = typename V::ValueType>
struct base_type { typedef U type; };
template < typename V >
struct base_type< V, V > { typedef V type; };
static_assert( std::is_same < typename base_type< myClass >::type , double >::value,
"base_type doesn't work" ); //This works.
static_assert( std::is_same < typename base_type< double >::type , double >::value,
"base_type doesn't work" ); //This returns "error: 'double' is not a class, struct, or union type"
Однако это не работает, второй static_assert терпит неудачу. Очевидно, что второе определение никогда не называется, но я не уверен, почему (оно определенно будет соответствовать лучше, чем первое).
Любой идеи?
5 ответов:
Альтернативно
template < typename V, typename = void> struct base_type { typedef V type; }; template < typename V > struct base_type< V, typename base_type<void, typename V::ValueType>::type > { typedef typename V::ValueType type; };
Как мы все любим эти грубые и уродливые хаки SFINAE:)
Вы должны использовать функцию для эффективного SFINAE.
Вот как это выглядит с новыми функциями C++11:
template <typename T> auto get_base_type(int) -> typename T::ValueType; template <typename T> auto get_base_type(...) -> T; template <typename T> struct base_type { using type = decltype(get_base_type<T>(0)); };
Из Википедии Следующее работает для меня, и я думаю, что это делает то, что вы хотите.
#include <type_traits> template <typename T> struct has_typedef_ValueType { // Types "yes" and "no" are guaranteed to have different sizes, // specifically sizeof(yes) == 1 and sizeof(no) == 2. typedef char yes[1]; typedef char no[2]; template <typename C> static yes& test(typename C::ValueType*); template <typename> static no& test(...); // If the "sizeof" the result of calling test<T>(0) would be equal to the sizeof(yes), // the first overload worked and T has a nested type named foobar. static const bool value = sizeof(test<T>(0)) == sizeof(yes); }; struct myClass { typedef double ValueType; }; template < class V, bool b = has_typedef_ValueType<V>::value > struct base_type { typedef typename V::ValueType type; }; template < typename V> struct base_type <V, false> { typedef V type; }; static_assert( std::is_same < typename base_type< myClass >::type , double >::value, "base_type doesn't work" ); //This works. static_assert( std::is_same < typename base_type< double >::type , double >::value, "base_type doesn't work" ); //This returns "error: 'double' is not a class, struct, or union type" int main() {}
#include <type_traits> #include <utility> template < typename V, typename=void> struct base_type { typedef V type; }; template < typename V > struct base_type< V, typename std::enable_if< std::is_same< typename V::ValueType, typename V::ValueType >::value >::type > { typedef typename V::ValueType type; };
Это довольно общий метод. Базовый случай имеет дополнительный неназванный параметр шаблона, по умолчанию равный
void
.Специализация делает
typename std::enable_if< expression >::type
вместо этогоvoid
, и это является допустимой специализацией, если это выражение является одновременно допустимым и истинным.В этом случае я сделал простой
std::is_same< V::value_type, V::value_type >::value
. В моем собственном коде, как известно, я пишуvalid_type< typename V::value_type >::value
, что просто:template<typename> struct valid_type:std::true_type {};
Класс всегда истинных черт.
Итак, Трюк
void
имеет некоторые проблемы. Но я нахожу его очень обобщенно.
double
не будет создавать экземпляр вашей специализации, потому что эта специализация вызывается только тогда, когдаbase_type
передается двум типам, и оба они идентичны. Второй тип необязателен, но когда он не предусмотрен, он заполняется значением по умолчаниюtypename V::ValueType
, и это, конечно, не удается дляdouble::ValueType
.Для полноты картины Ваша
base_type<V, V>
специализация будет создана для следующего типа:struct foo { typedef foo ValueType; }; static_assert(std::is_same<base_type<foo>::type, foo>::value, "Failed");