【问题标题】:function overloading and parameter inheritance函数重载和参数继承
【发布时间】:2021-03-31 06:44:58
【问题描述】:

在下面的代码中

#include <iostream>

using namespace std;

class A {

};

class B : public A {

};

class C {
public:
  void foo(const A& a) { cout << "A";}
  void foo(const B& b) { cout << "B";}
};
int main() {
    C c;
    const A& o = B();
    c.foo(o);
  
}

我希望结果是“B”,而不是“A”。 有什么方法可以在不求助于dynamic_cast 的情况下拨打void foo(const B&amp; b) 吗?

编辑:我想要实现的是让不同的函数从 C 中获取不同的资源(即一些成员变量)。我可以在 A 和 B 上使用虚函数并做这样的事情

void foo(const A& o) {
  o.bar(this)
}

然后A或B会从C那里收集资源,但我不想让A或B知道C。

【问题讨论】:

  • 重载什么也没听到。看看虚函数。
  • 好的,首先,行为是未定义的,因为const A&amp; o = B(); 存储了对已销毁对象的引用。其次,o 的 type 只知道是A 或某个子类的实例。它无法知道o实际上是指B实例。
  • @paddy - 没有任何未定义的东西。这属于参考寿命延长的情况。
  • @paddy 为什么它不知道?编译器知道 o 是 B 的子类。
  • 我猜是什么让 OP 想要 c.foo(o); 打印 B?编译器必须做正确的事情 (TM) 以延长临时 B 的生命周期?

标签: c++ overload-resolution


【解决方案1】:

在你说的评论中

为什么它不知道?编译器知道 o 是 B 的子类。

这里的措辞包含一个固有的误解。 o 不是B 的子类,而是一个引用。

就引用而言,C++ 类型系统不会对引用的初始化情况产生太大影响。它在一个简单的假设下运行,即引用绑定到已知类型的有效对象。在你的情况下,它是。它绑定到生命周期延长的B 的A 子对象。

之后,id 表达式o 将始终是A const 类型的左值。这就是类型系统的工作原理,这就是为什么重载解析总是更喜欢 print 的 A 的重载。所有表达式都有一个静态类型。

为了实现与您所追求的相似的效果,我们采用了围绕动态调度的设计模式。其中值得注意的是访问者模式和非虚拟接口模式(也称为 C++osphere 之外的“模板方法”)。

【讨论】:

    【解决方案2】:

    你的目标是这样的:

    What I want to achieve is for the different functions to obtain different resources from C (i.e some member variables).
    

    这是不使用dynamic_casts 的访问者模式的典型应用。请记住,这只适用于指针:

    #include <iostream>
    
    using namespace std;
    
    class A;
    class B;
    
    class C 
    {
    public:
        void foo(const A& a) { cout << "A";}
        void foo(const B& b) { cout << "B";}
    };
    
    struct Visitor
    {
        Visitor(C& c) : _C{c} {}   
    
        void Visit(A* a)
        {
            _C.foo(*a);
        }
    
        void Visit(B* b)
        {
            _C.foo(*b);
        }
    
    private:
        C& _C;
    };
    
    class A 
    {
    public:
        virtual void Accept(Visitor& visitor)
        {
            visitor.Visit(this);
        }
    };
    
    class B : public A 
    {
    public:
        void Accept(Visitor& visitor) override
        {
            visitor.Visit(this);
        }
    };
    
    int main() 
    {
        C c;
        A* a = new B();
        Visitor v(c);
        a->Accept(v);
    }
    

    【讨论】:

    • 你应该让你的访问者成为一个界面,所以A/B真的不知道C(尽管Visitor)。
    【解决方案3】:

    简单的方法确实是A中的虚方法:

    struct A {
      virtual ~A() = default;
      virtual void foo(const C& c) { std::cout << "A" << c.n;}
    };
    
    struct B : public A {
      void foo(const C& c) override { std::cout << "B" << c.n;}
    };
    
    struct C {
      int n = 42;
      void foo(const A& a) { a.foo(*this); }
    };
    
    int main() {
        C c;
        const A& o = B();
        c.foo(o);
    }
    

    但事实上,A/B 知道C。

    您可以使用访问者模式反转依赖关系(但访问者应该知道整个层次结构):

    struct A;
    struct B;
    
    struct IVisitor
    {
        virtual ~IVisitor() = default;
        virtual void visit(A&) = 0;
        virtual void visit(B&) = 0;
    };
    
    struct A {
      virtual ~A() = default;
      virtual void accept(IVisitor& v) { v.visit(*this); }
    };
    
    struct B : public A {
      void accept(IVisitor& v) override { v.visit(*this); }
    };
    
    class C {
    public:
      void foo(const A& a) { cout << "A";}
      void foo(const B& b) { cout << "B";}
    };
    
    struct Visitor : IVisitor
    {
        C& c;
        void visit(A&) override { c.foo(a); }
        void visit(B&) override { c.foo(b); }
    };
    
    int main() {
        C c;
        const A& o = B();
        o.accept(Visitor{c});
    }
    

    从 C++17 开始,std::variant 可能有助于调度,因为它提供了std::visit:

    struct A {};
    struct B {}; // inheritance no longer required.
                 // If you keep inheritance,
                 // virtual std::varaint<A*, B*> to_variant() { return this; }
                 // might be useful
    
    using A_or_B = std::variant<A, B>;
    
    class C {
    public:
      void foo(const A& a) { cout << "A";}
      void foo(const B& b) { cout << "B";}
    };
    
    int main()
    {
        C c;
        const A_or_B o = B();
        std::visit([&c](auto& a_or_b) { c.foo(a_or_b); }, o);
    }
    

    【讨论】:

    • 非常有趣。
    猜你喜欢
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 2021-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-05
    相关资源
    最近更新 更多