【问题标题】:recursive inheritance for overloading a method for a set of template arguments用于为一组模板参数重载方法的递归继承
【发布时间】:2017-07-08 09:54:08
【问题描述】:

我需要找到一种方法在给定一组模板参数的情况下递归构建一个类,以便该类继承自自身并为模板参数列表中的当前第一个模板参数构建一个方法f,然后继承自自己通过传递列表的其余部分。

所以,基本上我想为一个类C实现如下接口:

C<T1, T2, T3> c;

c 现在有方法C::f(T1)C::f(T2)C::f(T3)

到目前为止,我的方法是这样的:

// primary template
template <class H, class...>
class C {};

// base case where class... is empty
template <class H, class...>
class C<H>
{
public:
    void f(const H& h){
        // std::cout << typeid(h).name() << "\n";
    }
};

// recursive case where T is nonempty
template <class H, class... T>
class C : public C<T...>
{
public:
    void f(const H& h){
        // std::cout << typeid(h).name() << "\n";
    }
};

据我所知,这实际上并没有编译

错误:重新定义“C”类 C:公共 C

我的方法基本上是可行的,只是语义和/或语法上无效的代码问题,还是这种方法原则上不起作用?

【问题讨论】:

    标签: c++ inheritance recursion overloading variadic-templates


    【解决方案1】:

    对于初学者来说,类不能从自身继承。

    其次,您显然想要完成的只是让每个模板参数生成一个以该类作为参数的类方法。

    在这种情况下,这样的事情应该可以工作。

    template<typename ...> class C;
    
    template<>
    class C<> {};
    
    template<typename T, typename ...Args>
    class C<T, Args...> : public C<Args...> {
    
    public:
    
        void f (const T &)
        {
           // Whatever...
        }
    };
    

    请注意,这不是从自身继承的类。它是从另一个模板实例继承的模板实例。每个模板实例都是一个唯一的类。

    请注意,您在此处对所讨论的类方法有一个定义,而不是您尝试做的两个定义。这是一个小小的改进。

    另一个改进是考虑以这种方式重新排列类层次结构,如果可以考虑到您的其他类要求的话:

    template<typename T> class F {
    public:
    
        void f (const T &)
        {
        }
    };
    
    
    template<typename ...> class C;
    
    template<>
    class C<> {};
    
    template<typename T, typename ...Args>
    class C<T, Args...> : public C<Args...> , public F<T> {
    
    };
    

    使用这种方法,无论您使用C&lt;int, float&gt;,还是C&lt;int, char *&gt;,类方法都将始终声明为F&lt;int&gt; 的方法。这会稍微减少生成的代码浮点数,因为例如,包含int 的任何C 实例都将生成一个类方法实例,而不是像C&lt;int, float&gt;::f(const int &amp;)C&lt;int, char *&gt;::f(const int &amp;) 这样的两个单独的方法,这将, 否则完全相同。

    【讨论】:

    • 谢谢,但你的第二个设计我得到一个错误:error: member 'f' found in multiple base classes of different types
    • 可以通过在主模板中添加using F&lt;T&gt;::f;using C&lt;Args...&gt;::f; 来解决它。
    • 好的,我会试试的,但我注意到了别的东西:在你为以下程序设计的第一个int main(){ C&lt;char, int, double&gt; c; int i = 10; char ch = 'a'; c.f(i); c.f(ch); }我得到输出c c...意味着第一个模板的唯一重载参数被调用。请注意,我在C::f 中有std::cout &lt;&lt; typeid(T).name() &lt;&lt; "\n";
    • 您可能还需要using。我的回答仅限于声明方法,这是唯一被问到的事情。
    • 嗯,很抱歉,但我还不太明白。为什么我需要using?如果该类继承了其他专业化,那么为什么需要在某处使用using
    【解决方案2】:

    作为一种替代方法,我提出了一种基于 mixins 的解决方案。请随意忽略 type_name 类引入的样板,其目的是向您展示正确的部分是在每个参数基础上选取的。

    它遵循一个最小的工作示例:

    #include<type_traits>
    #include<utility>
    #include<iostream>
    
    template<typename> struct type_name;
    template<> struct type_name<int> { static const char *name; };
    template<> struct type_name<double> { static const char *name; };
    template<> struct type_name<char> { static const char *name; };
    
    const char * type_name<int>::name = "int";
    const char * type_name<double>::name = "double";
    const char * type_name<char>::name = "char";
    
    template<typename T>
    struct Part {
        void func(T t) {
            std::cout << type_name<T>::name << ": " << t << std::endl;
        }
    };
    
    template<typename... T>
    struct S: private Part<T>... {
        template<typename... Args>
        void f(Args&&... args) {
            using acc_t = int[];
            acc_t acc = { 0, (Part<std::decay_t<Args>>::func(std::forward<Args>(args)), 0)... };
            (void)acc;
        }
    };
    
    int main() {
        S<int, double, char> s;
        s.f(42);
        s.f(0.1);
        s.f('c');
        s.f('a', 0.3, 23);
    }
    

    这种方法的一些优点:

    • Part&lt;T&gt; 只为任何T 定义一次,无论您在不同的包中使用多少次。

    • S&lt;T, T&gt; 在编译时被拒绝,更一般而言,所有那些包含相同类型两次或更多次的包。否则它们会产生f(T) 的多个定义,随后的调用可能会模棱两可。

    • 您可以根据要求使用单个参数调用f。无论如何,如示例所示,您可以使用N 参数调用f,并且该调用等效于N 使用单个参数调用f
      换句话说,你可以使用这个:

      s.f('a', 0.3, 23);
      

      或者这个:

      s.f('a');
      s.f(0.3);
      s.f(23);
      

      两种情况下的结果都是一样的。
      如果需要,可以通过如下定义 S 轻松关闭此功能:

      template<typename... T>
      struct S: private Part<T>... {
          template<typename U>
          void f(U &&u) {
              Part<std::decay_t<U>>::func(std::forward<U>(u));
          }
      };
      

    查看它在 wandbox 上运行。


    附带说明,这是在 C++14 中用于模拟折叠表达式的常用技巧:

    template<typename... Args>
    void f(Args&&... args) {
        using acc_t = int[];
        acc_t acc = { 0, (Part<std::decay_t<Args>>::func(std::forward<Args>(args)), 0)... };
        (void)acc;
     }
    

    您可以在 SO 和网络上找到更多相关信息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-03-18
      • 1970-01-01
      • 1970-01-01
      • 2011-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多