3

我试图从ValueType我定义的类(typedef)中获取 typedef 指向的类型。但是,当这失败时,我希望它返回给定的类型(例如,如果我给它 a double,我希望它返回 a 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 失败。显然,第二个定义从未被调用,但我不确定为什么(它肯定会比第一个匹配得更好)。

有任何想法吗?

4

5 回答 5

5

您必须使用有效的 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)); };
于 2013-03-13T18:27:17.020 回答
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 黑客 :)

于 2013-03-13T18:30:39.947 回答
2

来自维基百科 以下内容对我有用,我认为它可以满足您的需求。

#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() {}
于 2013-03-13T18:24:23.143 回答
2
#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,并且它是一个有效的特化 IFF,即表达式既有效又为真。

在这种情况下,我做了一个简单的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技巧有一些问题。但我觉得它很一般。

于 2013-03-13T18:28:40.140 回答
1

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");
于 2013-03-13T18:29:52.067 回答