具有std :: enable_if的C ++可变参数模板部分模板专业化

时间:2019-05-12 22:07:18

标签: c++ templates c++14 variadic-templates template-meta-programming

ITNOA

我的问题是在变参模板部分模板专业化方案中如何使用std::enable_if

例如,我有一个使用可变模板部分特化的类,如下所示:

        /**
         *  Common case.
         */
        template<typename... Args>
        struct foo;

        /**
         *  Final superclass for foo.
         */
        template<>
        struct foo<>{ void func() {} };

        /**
         *  Regular foo class.
         */
        template<typename H, typename... T>
        struct foo<H, T...> : public foo<T...> {
            typedef super foo<T...>;
            void func() {
                cout << "Hi" << endl;
                this->super::template func();
            }
        }

一切正常,但是如果H是整数类型,我想进行特定的部分专业化,因此我在下面添加了新代码

        enum class enabled {_};
        template<typename H, typename... T>
        struct foo<H, typename std::enable_if<std::integral_type<H>::value,enabled>::type = enabled::_, T...> : public foo<T...> {
            typedef super foo<T...>;
            void func() {
                cout << "Hooray Inegral" << endl;
                this->super::template func();
            }
        }

但是上面的代码不起作用,我的问题是如何像上面那样做?

1 个答案:

答案 0 :(得分:2)

enable_if<bool b, T=void>是一个类模板,用于定义type=T是否为b==true。因此,enable_if<b>::type仅在b==true时有效。 SFINAE指出替换失败不是错误。恕我直言not_disable_if会更合适。

部分专业化通过针对当前已解析类型的模式匹配模板进行。您不能在其中添加新的模板参数,因为它将匹配不同的东西。 struct foo<H,std::enable_if_t<...,void>>仅在foo<H,void>可以扣除... 且得出H的情况下才匹配true

struct foo<std::enable_if_t<std::is_integral_v<H>,H>>不能简单地工作,因为无法从中推断出H foo<int>。它必须以某种方式推导enable_ifis_integral的语义,并看到如果使用H=int,那么它将解析为foo<int>,这当然是通常不能完成的。 / p>

SFINAE仅可应用于过载解析期间考虑的那些零件。第一个和您使用的第一个是类模板参数,但是如上所述,这将更改其实际匹配的内容。另一个选项是模板参数本身。但是C ++不允许用于类专门化的默认模板参数,通常用于此目的。没有函数中的返回值。 SFINAE在类主体或其基类内不使用任何内容。我不认为您想要的是当前设置。

我将提供一些重新设计:

#include <iostream>
#include <type_traits>

// Only specifies behaviour for the head part - one T
// Ts... can be ignored, but are required.
//  - I left them because I'm not sure whether you want to use them in the real code.
//  - But they are required because `foos` use them as base classes and they must be unique.
template<typename T,bool is_integral,typename...Ts>
struct foo_impl;

template<typename T,typename...Ts>
struct foo_impl<T,true,Ts...>
{
    void func() {
        std::cout << "Hooray Inegral" << std::endl;
    }
};

template<typename T,typename...Ts>
struct foo_impl<T,false,Ts...>
{
    void func() {
        std::cout << "Hi" << std::endl;
    }
};

template<typename T,typename...Ts>
using foo = foo_impl<T,std::is_integral<T>::value,Ts...>;

template<typename...Ts>
struct foos;

template<typename H,typename...Ts>
struct foos<H,Ts...> : private foo<H,Ts...>, public foos<Ts...>
{
   using head = foo<H,Ts...>;
   using tail = foos<Ts...>;
   //Only named differently for clarity, feel free to rename it back to 'func'
   void rec_func()
   {
       //If we inherited from foo<H> so this was only this->foo<H>::func() then
       //foos<int,int> would have two foo<int> base classes and this call would be ambigious.
       this->head::func();
       this->tail::rec_func();
   }
};

template<> struct foos<>{
    void rec_func(){}
};

int main()
{
    foos<int,int,char,double> x;
    x.rec_func();
}

foo仅处理一个T,并且需要专门化。您可以将foo<T,false>foo<T,true>之间的任何常见行为提取到一个公共基类中。当前,foofoos API之间存在重复。但是foo'API可以被视为私有API,并且可以有所不同,因此我完全不会说这是不利的。正如我在Ts...中所说的foo_impl一样。如果您不需要它们,可以通过以下方式将其删除:通过简单地从std::tuple<foo<Ts>...>和一些折叠表达式(C ++ 17)/ magic(c ++ 14)派生来调用func函数。我可以根据需要添加。