1

我想class C从一组 types 中的每种类型构造我的类...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类。

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

4

2 回答 2

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)
{  }

更神奇的是,您甚至可以遍历列表中的每个元素并查看构造函数参数是否可转换为列表项,但这可能与您想要的不同。

于 2013-04-27T13:57:19.923 回答
1

这个特征类适用于任何类型的包,无论是它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存在 iffT位于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将二进制boolean 模板作为参数的 a,并在列表中查找第一个匹配的模板。

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;

因为如果您不这样做,并且您的类型是由 an 构造的,int并且有人传入 ashort或 anunsigned int或其他任何内容,那么您的 SFINAE 代码将拒绝从您的类型列表中不完美的东西构造。

于 2013-04-27T14:20:56.380 回答