【问题标题】:Specializing class with SFINAE if a parameter pack is needed如果需要参数包,则使用 SFINAE 专门化类
【发布时间】:2016-05-17 16:39:25
【问题描述】:

当我得到这个问题的完美答案时: 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( )   {}
};

【问题讨论】:

  • “构造函数不能重载”问题可以通过向其中一个构造函数添加一个虚拟和默认模板参数(如, typename Z = void)来解决,但最后的代码sn-p 说“如果 T 没有定义这个 trait,应该使用”,你的意思是如果 ::TRAIT 不存在?
  • @Holt 不,特征是一个自己的模板类,它也使用一个参数包。使用 sfinae 我必须检查该类型的参数包是否为空。我避免了这种特殊的事情,以使其在这里不会变得更复杂。
  • 您能否向我们展示带有非工作重载构造函数的完整代码,因为正如@PiotrSkotnicki 提到的,您的问题似乎包含两个不同的问题(检查TRAIT 是否存在并检查TRAIT 是否为特定类型)。
  • @Holt:对不起,我在问题中更正了这一点。我需要检查我的特质是否满足某些条件。该特征始终存在。
  • @PiotrSkotnicki 好的,我测试了这个想法。我想知道 sfinae 发生在检查重载规则之后,如果只有一个 SFINAE 表达式有效,这也会导致相同函数的两倍。

标签: c++ c++14 sfinae


【解决方案1】:

而不是

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; }

【讨论】:

    【解决方案2】:
    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&lt;T, types_tag&lt;int, double, char&gt;

    您可以编写如下适配器:

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

    我们使using 别名的名称​​比实现struct 更好

    作为一种类型传递的类型捆绑可以使一大堆元编程变得简单。您可以传递多个捆绑包;并且您可以将 types_tag 按值作为参数传递给函数,以便轻松推断包的内容。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-03
      • 1970-01-01
      相关资源
      最近更新 更多