SFINAE类型包含类型

时间:2013-04-27 13:52:29

标签: c++ templates c++11 variadic-templates sfinae

我想在类型class C的每个类型中构建我的类...Tsizeof...(T)足够大,所以我不想写出所有构造函数变体,如下所示:

// T : {T1, T2, ..., TN}
struct C
{
    C(T1);
    C(T2);
    ...
    C(TN); // Phew! I'm tired.
};

所以我想尝试下一个:

// type_set is something like template< typename... T > struct type_set {...};
struct C 
{
    using constructible_from_types = type_set< int, double, short >;
    template< typename T0 >
    C(T0, typename std::enable_if< is_contained< T0, constructible_from_types >::value >::type * = nullptr);
};
// or
// type_set is something like template< typename... T > struct type_set { template< typename T0 > constexpr bool is_contained() {...} ...}
struct C 
{
    using constructible_from_types = type_set< int, double, short >;
    template< typename T0 >
    C(T0, typename std::enable_if< constructible_from_types::is_contained< T0 >() >::type * = nullptr);
};

但我不知道如何实现is_contained类型特征和type_set类。

而且,一般来说,对这个问题的解决方案的规范实施感兴趣。

2 个答案:

答案 0 :(得分:5)

首先,一个更好的构造函数:

template <typename T,
          typename = typename std::enable_if<is_contained<T, T0, T1, T2>::value>
C(T) { }

// or C(T const &), or even C(T &&)
// and use "typename std::decay<T>::type" instead of just "T"

现在的特点:

#include <type_traits>

template <typename T, typename ...> struct is_contained : std::false_type { };

template <typename T, typename Head, typename ...Tail>
struct is_contained<T, Head, Tail...> : std::integral_constant<bool,
    std::is_same<T, Head>::value || is_contained<T, Tail...>::value> { };

重复一遍,用法是:

is_contained<T, double, int, char>::value

如果您希望将类型列表包装到另一个类型中,这只是一个简单的修改,但我只使用std::tuple作为包装类型。


在评论之后,这是一个带有通用引用的构造函数:

template <typename T,
          typename = std::enable_if<
              is_contained<typename std::decay<T>::type,
                           T0, T1, T2, T3>::value
                                   >::type>
C(T && t)
{  }

有了更多的魔法你甚至可以遍历列表中的每个元素,看看构造函数参数是否可转换到列表项,但这可能与你想要的不同。

答案 1 :(得分:1)

此traits类适用于任何类型的包,无论是type_set还是std::tuple

#include <type_traits>
template<typename T, typename L> struct is_contained; // illegal
template<typename T, template<typename...>class L>
struct is_contained< T, L<> > : std::false_type {};
template<typename T, template<typename...>class L, typename T0, typename... Ts>
struct is_contained< T, L<T0, Ts...> > :
  std::integral_constant< bool,
    std::is_same<T, T0>::value
    || is_contained< T, L<Ts...> >::value
  >
{};

然而,我发现index_of更有用:

template<typename T, typename L, typename=void> struct index_of; // illegal
template<typename T, template<typename...>class L>
struct index_of< T, L<>, void > {}; // SFINAE enable
template<typename T, template<typename...>class L, typename T0, typename... Ts>
struct index_of<
  T,
  L<T0, Ts...>,
  typename std::enable_if<!std::is_same<T, T0>>::type
> : std::integral_constant< std::size_t, 1 + index_of< T, L<Ts...> >::value >
{};
template<typename T, template<typename...>class L, typename T0, typename... Ts>
struct index_of<
  T,
  L<T0, Ts...>,
  typename std::enable_if<std::is_same<T, T0>>::type
> : std::integral_constant< std::size_t, 0 >
{};

还允许您通过SFINAE检查是否存在index_of<T,List>::value如果T中存在List

接下来,我将在构造函数上启用完美转发:

template<typename T>
using RemoveRefCv = typename std::remove_cv< typename remove_ref< T >::type >::type;
struct C 
{
  using constructible_from_types = std::tuple< int, double, short >;
  template< typename T, std::size_t=std::index_of<RemoveRefCv<T0>>::value >
  C(T&&);
};

(假设您不必担心可以从const而不是列表中的非const构建。

您可以更进一步,接受任何可转换为列表中任何类型的T。这可能需要一些智能来确定您如何处理冲突。一个简单的方法可能是找到一个完美的匹配,并且找不到第一个可以转换的方法。

如果您查看index_of,您会注意到它通过查询std::is_same完成了大部分工作。您可以编写一个search_for,它将二进制bool ean模板作为参数,并在列表中查找匹配的第一个。

template<template<typename, typename>class Func, typename T, typename L, typename=void> struct search_for; // illegal
template<template<typename, typename>class Func, typename T, template<typename...>class L>
struct search_for< Func, T, L<>, void > {}; // SFINAE enable
template<
  template<typename, typename>class Func,
  typename T,
  template<typename...>class L,
  typename T0,
  typename... Ts
>
struct search_for<
  Func,
  T,
  L<T0, Ts...>,
  typename std::enable_if<!Func< T, T0 >::value
> : std::integral_constant<
  std::size_t,
  1 + search_for< Func, T, L<Ts...> >::value
> {};
template<
  template<typename, typename>class Func,
  typename T,
  template<typename...>class L,
  typename T0,
  typename... Ts
>
struct search_for<
  Func,
  T,
  L<T0, Ts...>,
  typename std::enable_if<Func<T, T0>::value>::type
> : std::integral_constant< std::size_t, 0 >
{};
template<template<typename,typename>class Func, typename T, typename L>
using SearchFor = search_for<Func, T, L>;

然后我将根据以下内容重写index_of

template<typename T, typename List>
struct index_of:SearchFor<std::is_same, T, List> {};

然后编写一个尝试index_of的类,如果失败尝试search_for< std::is_constructable, ... >,则提取该索引,然后使用该类型将传入的参数转换为列表中的索引类型:

template<std::size_t n, typename L>
struct get_type {};// SFINAE enabled
template<std::size_t n, template<typename...>class L>
struct get_type< n, L<> > {};
template<template<typename...>class L, typename T0, typename... Ts>
struct get_type< 0, L<T0, Ts...> > {
  typedef T0 type;
};
template<std::size_t n, template<typename...>class L, typename T0, typename... Ts>
struct get_type< n, L<T0, Ts...> >:
  get_type< n-1, L<Ts...> >
{};
template<std::size_t n, typename List>
using GetType = typename get_type<n, List>::type;

因为如果您不这样做,并且您的类型是由int构建而有人通过shortunsigned int或其他任何内容,那么您的SFINAE代码将拒绝被拒绝由你的类型列表中不完美的东西构成。