我想在类型class C
的每个类型中构建我的类...T
。 sizeof...(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
类。
而且,一般来说,对这个问题的解决方案的规范实施感兴趣。
答案 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
构建而有人通过short
或unsigned int
或其他任何内容,那么您的SFINAE代码将拒绝被拒绝由你的类型列表中不完美的东西构成。