【问题标题】:Non-Virtual Polymorphism in C++C++ 中的非虚拟多态性
【发布时间】:2025-11-28 00:35:01
【问题描述】:

我已经开发了以下代码来尝试实现非虚拟多态性:

#include <functional>
#include <iostream>
namespace{
    using std::function;
    class base {
    protected:
        using signiture =void(void);
        using func_t = function<signiture>;
        ~base(){}
    public:
        func_t bar;//std::function object to a function of type "signature"
    };
}
template <typename implementation>
class foo:public base {
public:
    foo(implementation* instance){
        bar = func_t(std::bind(&implementation::bar_implementation,instance));
//binds a function of name "bar_implementation" from class implementation to the std::function object
//binds to object "instance"
    }
};
typedef base foo_base;

class testcase:public foo<testcase> {
public:
    friend class foo;//allows implementations to be protected or private
    testcase():foo(this){}//sends instance to the constructor of the base class in order to enable binding
protected:
    void bar_implementation(void){
        std::cout<<"Hello"<<std::endl;
    }
};
class testcase2:public foo<testcase2> {
public:
    friend class foo;//allows implementations to be protected or private
    testcase2():foo(this){}
protected:
    void bar_implementation(void){
        std::cout<<"World!"<<std::endl;
    }
};


int main(int argc, const char * argv[]) {
    testcase t;
    testcase2 t2;

    foo_base* b = &t;
    foo_base* b2 = &t2;
    b->bar();
    b2->bar();
    return 0;
}

实际上,这段代码分散在几个文件中...

我想知道我的代码中的任何内容是否可以被视为不良做法、未定义的行为或在某些庄园中是不受欢迎的? A Live Example

对这种模式作为虚拟继承和设计的替代品的任何想法都值得赞赏。

如果我能澄清任何事情,请告诉我。

编辑: 我问这个问题是为了确定这样的设计是否有任何理由可以成为实现非虚拟多态性的合适方法,如果不是,为什么会这样?

【问题讨论】:

  • 问题:这段代码真的不是虚表多态,还是你自己滚?
  • 考虑到 std::function 在后台使用虚函数(擦除类型),我看不出这应该完成什么。
  • base::bar 在大多数实际用途中是一个单条目 vtable(只是调用成本更高)。练习的重点是什么?
  • 看起来您正在竭尽全力实现语言本身已经实现的东西,只有该语言可能更有效、更安全地实现它。
  • 我认为部分问题在于这看起来不像是“非虚拟”的东西。它看起来像一个虚拟函数调用系统的手动实现。

标签: c++ inheritance c++11 polymorphism


【解决方案1】:

您的代码是一种有趣的方法。这是一个很好的概念证明,但它有一个重大缺陷:它不能始终如一地管理继承

问题:类继承

假设我想创建一个类testcase22,它继承自testcase2,但有自己的bar() 实现。

备选方案1:如果我通过继承testcase2来天真地定义它,它将使用testcase2bar_implementation

    class testcase22:public testcase2  {
    public:
        testcase22() {}   // naive contructor
    protected:
        void bar_implementation(void){    // will not be used
            std::cout<<"Great!"<<std::endl;
        }
    };

备选方案 2:如果我尝试使用您的构造函数模式,由于编译错误,它将无法工作。因为类型推导会使用foo&lt;testcase2&gt;,它不是testcase22的直接基类,因此不能在mem-initializer列表中使用:

    class testcase22:public testcase2  {
    public:
        friend class foo;//allows implementations to be protected or private
        testcase22():foo(this){}   // !!!!! causes compile error
    protected:
        void bar_implementation(void){
            std::cout<<"Great!"<<std::endl;
        }
    };

备选方案 3:如果我显式使用 foo,它将无法工作,因为 foo os 也不是有效的基类型初始化程序。

替代方案 4:如果我尝试使用多重继承,我会遇到模棱两可的基类

    class testcase22:public testcase2, public foo<testcase22> {
    public:
        friend class foo;//allows implementations to be protected or private
        testcase22():foo(this){}
    protected:
        void bar_implementation(void){
            std::cout<<"Great!"<<std::endl;
        }
    };

这可以通过定义 foo 从 base 虚拟继承来解决:

    class foo:public virtual base { ... };

然后它会编译,但它仍然会使用testcase2bar_implementation 而不是来自testcase22 的那个。顺便说一句,即使它可以工作,虚拟基也会阻止对您的类族使用多重继承的可能性。

结论

你的好构造是有效的,但只能使用单级简单继承。

这是一个主要的限制。尤其是考虑到多态性的好处是管理自然层次结构以充分利用继承关系时。

与易于使用的虚函数相比,您的模式使用起来也相对复杂。使用多个多态函数(其中一些函数甚至重载)后,您的代码会是什么样子?

您打算使用您的模式实现的目标尚不清楚,但老实说,对于必须维持数年的实际项目,我不会采用这种方法。但这当然是我自己的主观意见。

【讨论】:

  • 这只是一个思想实验,所以如果它不起作用也没什么大不了的。你是对的,你可以使多重继承工作的唯一方法是将 testcase2 类编写为 CRTP 样式模板并将 testcase22 类型名一直传递给 foo 以及将指针传递给 testcase22 aka this 并拥有基础绑定到它。这不是处理我知道的事情的好方法