【问题标题】:C++ std::function to take functions with sub class parameterC++ std::function 获取带有子类参数的函数
【发布时间】:2021-12-09 22:29:11
【问题描述】:

[更新] 这个问题的原因: 有许多现有的 lambdas 定义为 [](const ChildType1& child),都在一个大注册表中。我们想在同一个注册表中注册新的 lambda,例如 [](const ChildType2& child)。如果我们使用Parent 定义函数包装器,对于许多现有的lambda,我们需要将它们更改为[](const Parent& someone),并在内部从Parent 向下转换为ChildType1


如果我有一个作为std::function<void(const Parent&)> 的函数包装器,有没有办法允许它采用Parent 子类作为参数的函数,例如[](const Child& child){...},其中ChildParent 的子类。

以下内容无法编译。在线IDElink.

#include <iostream>
#include <functional>

class Parent {
    public:
        virtual void say() const {
            std::cout<<"I am parent"<<"\n";
        }
};

class Child: public Parent {
    public:
        void say() const {
            std::cout<<"I am child"<<"\n";
        }
};

typedef std::function<void(const Parent&)> Wrapper;

int main() {
    Wrapper func=[](const Child& child){  // of course works if Child->Parent
      child.say();
    };
    
    Child c;
    func(c);
    return 0;
}

【问题讨论】:

  • 不,没有办法,这在逻辑上是错误的,因为可以将Parent 传递给需要Child 的函数。
  • 不,这是错误的方差。请参阅协变和逆变。
  • 你有什么不想要的原因Wrapper func=[](const Parent&amp; child) { .. };?您仍然可以使用Child 调用它(并且会调用它的say() 函数)。
  • 原因是有许多现有的 lambdas 定义为[](const ChildType1&amp; child),都在一个大注册表中。我们想在同一个注册表中注册新的 lambda,例如 [](const ChildType2&amp; child)。如果我们使用Parent 定义函数包装器,那么在许多现有的lambda 中,我们需要将Parent 向下转换为ChildType1
  • @Robert 我不明白你为什么需要在这里沮丧。你有多态性,所以你可以使用它。

标签: c++ function polymorphism subclass std-function


【解决方案1】:

为什么不允许这样做?

语言不允许这样做,因为这可能会导致不一致。

根据您对Wrapper 的定义,以下代码应该是合法的:

Wrapper f; 
Parent x; 
... // Initialize f with a legitimate function dealing Parent 
f(x); 

现在想象两个类:

class Child1: public Parent {
    public:
        void say() const {
            std::cout<<"I am child1"<<"\n";
        }
        virtual void tell() const {
            std::cout<<"This is specific to child1"<<"\n";
        }
};
class Child2: public Parent {
    public:
        void say() const {
            std::cout<<"I am child2"<<"\n";
        }
};

以下代码也是有效的,因为 Child1Child2 派生自 Parent

Child1 y; 
Child2 z; 
f(y);   
f(z);

如果你被允许为你的包装器分配一个带有子参数而不是父参数的函数,你也可以这样做:

Wrapper f=[](const Child1& child){  // if this is legitimate
  child.tell();                     //   then this would be legitimate
};

你会很容易猜到f(x)f(z) 不会工作,尽管f 的类型应该允许它。

有解决办法吗?

你可以做的,但这是更冒险的事情,就是制作一个包装函数,它接受 Parent 参数,向下转换为 Child。但我不推荐它,除非没有其他解决方案,而且只能格外小心。

using Wrapper = std::function<void(const Parent&)>;

int main() {
    Wrapper func=[](const Parent& parent){
      auto child=dynamic_cast<const Child*>(&parent);  
      if (child)
        child->say();
      else std::cout<<"OUCH!!! I need a child"<<std::endl; 
    };
    
    Parent x; 
    Child c;
    func(c);
    func(x); 
}

Demo

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-02
    • 1970-01-01
    • 2022-01-23
    • 2012-02-21
    • 2019-12-13
    • 2023-03-23
    • 1970-01-01
    相关资源
    最近更新 更多