代码之家  ›  专栏  ›  技术社区  ›  sorosh_sabz

C++ ValIDIC模板局部模板特化与EnableαIF

  •  1
  • sorosh_sabz  · 技术社区  · 5 年前

    伊图诺

    我的问题是如何使用 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();
                }
            }
    
    

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

    0 回复  |  直到 5 年前
        1
  •  2
  •   Quimby    5 年前

    enable_if<bool b, T=void> 是定义 type=T 如果 b==true . 所以, enable_if<b>::type 只有当 B==真 . sfinae声明替换失败不是错误。IMHO 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_if is_integral 看看如果 H=int ,然后它精确地解析为 foo<int> 当然,这在一般情况下是不能做到的。

    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> 成为一个公共的基类。目前在 foos 应用程序编程接口。但是 API可以被视为私有的,也可以是不同的,所以我不会说这是一个缺点。正如我解释的那样 Ts... 在里面 foo_impl 是必需的。如果你不需要它们,它们可以通过-例如简单地从 std::tuple<foo<Ts>...> 和一些折叠表达式(C++ 17)/魔术(C++ 14)调用 func 功能。如果你愿意的话,我可以补充一下。