如果需要参数包,请使用SFINAE专业课程

时间:2016-05-17 16:39:26

标签: c++ c++14 sfinae

我对这个问题得到了完美答案: Specializing class with SFINAE

为了完整性,我再次在此插入正确的解决方案:

class AA { public: using TRAIT = int; };
class BB { public: using TRAIT = float; };

template < typename T, typename UNUSED = void> class X;

template < typename T >
class X<T, typename std::enable_if< std::is_same< int, typename T::TRAIT>::value, void >::type>
{
    public: 
        X() { std::cout << "First" << std::endl; }
};

template < typename T > 
class X<T, typename std::enable_if< !std::is_same< int, typename T::TRAIT>::value, void >::type>
{   
    public:
        X() { std::cout << "Second" << std::endl; }
};

int main()
{
     X<AA> a;
     X<BB> b;
}

但是如果我必须使用参数包进一步使用,我认为没有机会写下这些东西:

template < typename T, typename ...S, typename UNUSED = void> class X;
  

错误:参数包'S'必须位于模板参数列表的末尾

定义的顺序与

不同
template < typename T, typename UNUSED = void, typename ...S> class X;
如果第一个附加类型正在使用,

会出现问题。

好的,我所描述的是一种我实际上找不到的技术解决方案。也许有一个不同的。我的根本问题是什么:我需要2个不同的构造函数来调用不同的基类构造函数。但是因为两个构造函数都有相同的参数集,所以我认为没有机会专门构造构造函数。

如果specialize构造函数可以工作,它可以是这样的:

template < typename T>
class Y
{
    public:
        template <typename U = T, typename V= typename std::enable_if< std::is_same< int, typename U::TRAIT>::value, int >::type>
            Y( const V* =nullptr) { std::cout << "First" << std::endl; }

        template <typename U = T, typename V= typename std::enable_if< !std::is_same< int, typename U::TRAIT>::value, float >::type>
            Y( const V* =nullptr) { std::cout << "Second" << std::endl; }

};

  

错误:'模板模板Y :: Y(const V *)'无法重载

但正如已经提到的......我不知道是否可以做到。

为了显示潜在的问题,我将给出以下示例,该示例显示了依赖于基类中定义的特征的基类构造函数的不同用法。

template <typename T, typename ... S>: public T
class Z
{
    public:
        // should work if T defines a trait
        Z( typename T::SomeType t): T( t ) {}
        // should be used if T defines another trait
        Z( typename T::SomeType t): T( )   {}
};

2 个答案:

答案 0 :(得分:2)

而不是

template < typename T, typename ...S, typename UNUSED = void> class X;

你可以添加一个图层:

template <typename T, typename Dummy = void, typename ... Ts> class X_impl {};

然后

template <typename T, typename ...Ts>
using X = X_impl<T, void, Ts...>;

对于SFINAE,由于默认模板参数不是签名的一部分,

template <typename U = T,
          typename V = std::enable_if_t<std::is_same<int, typename U::TRAIT>::value, int>>
 Y(const V* = nullptr) { std::cout << "First" << std::endl; }

template <typename U = T,
          typename V = std::enable_if_t<!std::is_same<int,
                                                      typename U::TRAIT>::value, float>>
Y(const V* = nullptr) { std::cout << "Second" << std::endl; }

应该重写,例如:

template <typename U = T,
          std::enable_if_t<std::is_same<int, typename U::TRAIT>::value>* = nullptr>
 Y() { std::cout << "First" << std::endl; }

template <typename U = T,
          std::enable_if_t<!std::is_same<int, typename U::TRAIT>::value>* = nullptr>
Y() { std::cout << "Second" << std::endl; }

答案 1 :(得分:0)

template<class...>struct types_tag{using type=types_tag;};
template<class...Ts>constexpr types_tag<Ts...> types{};

这些帮助程序允许您作为一个包,一个参数传递许多类型。

现在您的类型X可能如下所示:

template<class T, class types, class=void>
class X;
template<class T, class...Ts>>
class X<T, types_tag<Ts...>, std::enable_if_t<true>> {
};

X的用户传入X<T, types_tag<int, double, char>

您可以编写如下的适配器:

template<class T, class...Ts>
using X_t = X<T, types_tag<Ts...>>;

我们使using别名的名称​​更好,而不是实现struct

作为一种类型传递的类型捆绑可以使一大堆元编程变得简单。你可以传递多个包;并且您可以将types_tag by-value作为参数传递给函数,以便轻松扣除该包的内容。