Получив определение типа шаблона параметра, или если это невозможно, становится тип самого


Я пытаюсь получить тип, на который указывает 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 3

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");