【问题标题】:Using boost::function with a parameter to shared pointer to derived class使用带有参数的 boost::function 指向派生类的共享指针
【发布时间】:2018-08-25 19:54:05
【问题描述】:

在 Ubuntu 16.04 上使用 C++ 和 g++ 5.4.0。

我有一个 A 类和一个从 A 类派生的 B 类。函数 f1 将指向 A 类的共享指针作为参数。函数 f2 将指向 B 类的共享指针作为参数,并返回与 f1 相同的类型。使用 boost::function,另一个函数 F 将 f1 等函数作为参数。代码如下所示:

result_t f1 ( const boost::shared_ptr<A> a );
result_t f2 ( const boost::shared_ptr<B> b );
typedef boost::function < result_t (const boost::shared_ptr<A>&) > f1_t;
void F ( const f1_t f );

使用 f1 作为参数调用 F 可以正常工作。我现在想调用 F 但以 f2 作为参数。我收到以下错误:

error: invalid initialization of reference of type const boost::shared_ptr<B>& from expression of type const boost::shared_ptr<A>
           return f(BOOST_FUNCTION_ARGS);

确实不需要 F 得到这个错误,这样做:

f1_t f = f2;

给出同样的错误。

【问题讨论】:

  • 那么...问题是什么?请注意,对于 gcc 5.4.0,您可以使用 -std=c++11std::function/std::shared_ptr
  • 我认为设计中有代码味道。你不能在shared_ptr&lt;A&gt; 上调用f2,除非它实际上指向B

标签: c++ boost shared-ptr derived-class


【解决方案1】:

函数原型没有协方差。不同的签名就是:不同的类型。

在这种情况下,您需要使用转换包装器来包装函数。

让我们创建一些设施定义:

using result_t = int;
struct A { };
struct B : A { };

typedef boost::shared_ptr<A> APtr;
typedef boost::shared_ptr<B> BPtr;

result_t f1(APtr) { return 1; }
result_t f2(BPtr) { return 2; }

typedef boost::function <result_t(APtr const&)> funOfA;
typedef boost::function <result_t(BPtr const&)> funOfB;

现在将 funOfB 包装为 funOfA 如下所示:

funOfA wrapFunOfB(const funOfB f) {
    struct {
        funOfB _f;
        result_t operator()(APtr const& a) const { 
            return _f(boost::static_pointer_cast<B>(a));
        }
    } wrap { f };

    return wrap;
}

现在你可以轻松写了:

int main() {
    F(f1);
    F(wrapFunOfB(f2));
}

简单的 C++03 演示

Live On Coliru

#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/function.hpp>
#include <iostream>

typedef int result_t;
struct A { int i; };
struct B : A { int j; };

typedef boost::shared_ptr<A> APtr;
typedef boost::shared_ptr<B> BPtr;

result_t f1(APtr) { return 1; }
result_t f2(BPtr) { return 2; }

typedef boost::function <result_t(APtr const&)> funOfA;
typedef boost::function <result_t(BPtr const&)> funOfB;

struct Wrapper {
    typedef result_t result_type;
    funOfB _f;

    result_t operator()(APtr const& a) { 
        return _f(boost::static_pointer_cast<B>(a));
    }
};

funOfA wrapFunOfB(const funOfB f) {
    Wrapper wrap = { f };
    return wrap;
}

void F(const funOfA f) {
    APtr a = boost::make_shared<A>();
    APtr b = boost::make_shared<B>();

    //std::cout << "f(a): " << f(a) << "\n"; // UNDEFINED BEHAVIOUR if f wraps a funOfB
    std::cout << "f(b): " << f(b) << "\n";
}

int main() {
    F(f1);
    F(wrapFunOfB(f2));
}

打印

f(b): 1
f(b): 2

问题,警告:dynamic_pointer_cast&lt;&gt;

如果F 实际调用的对象上的参数实际上 不是B,则static_cast&lt;&gt; 将调用Undefined Behaviour

如果您想防止这种情况发生,请使用dynamic_pointer_cast,它要求类AB多态类型

Live On Coliru

#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/function.hpp>
#include <iostream>

typedef int result_t;
struct A     { int i; virtual ~A() {} };
struct B : A { int j; };

typedef boost::shared_ptr<A> APtr;
typedef boost::shared_ptr<B> BPtr;

result_t f1(APtr a) { return a?1 : 0; }
result_t f2(BPtr b) { return b?2 : -99; }

typedef boost::function <result_t(APtr const&)> funOfA;
typedef boost::function <result_t(BPtr const&)> funOfB;

struct Wrapper {
    typedef result_t result_type;
    funOfB _f;

    result_t operator()(APtr const& a) { 
        return _f(boost::dynamic_pointer_cast<B>(a));
    }
};

funOfA wrapFunOfB(const funOfB f) {
    Wrapper wrap = { f };
    return wrap;
}

void F(const funOfA f) {
    APtr a = boost::make_shared<A>();
    APtr b = boost::make_shared<B>();

    std::cout << "f(a): " << f(a) << "\n";
    std::cout << "f(b): " << f(b) << "\n";
}

int main() {
    F(f1);
    F(wrapFunOfB(f2));
}

打印

f(a): 1
f(b): 1
f(a): -99
f(b): 2

C++11 版本

这里的事情变得更加优雅。值得注意的是,Wrapper 类可以是本地的和匿名的:

funOfA wrapFunOfB(const funOfB f) {
    struct {
        typedef result_t result_type;
        funOfB _f;

        result_t operator()(APtr const& a) { 
            return _f(std::dynamic_pointer_cast<B>(a));
        }
    } wrap { f };
    return wrap;
}

下一级:改用 lambda:

funOfA wrapFunOfB(const funOfB f) {
    return [f](APtr const& a) { return f(std::dynamic_pointer_cast<B>(a)); };
}

Live On Coliru

#include <memory>
#include <functional>
#include <iostream>

typedef int result_t;
struct A     { int i; virtual ~A() {} };
struct B : A { int j; };

typedef std::shared_ptr<A> APtr;
typedef std::shared_ptr<B> BPtr;

result_t f1(APtr a) { return a?1 : 0; }
result_t f2(BPtr b) { return b?2 : -99; }

typedef std::function<result_t(APtr const&)> funOfA;
typedef std::function<result_t(BPtr const&)> funOfB;

funOfA wrapFunOfB(const funOfB f) {
    return [f](APtr const& a) { return f(std::dynamic_pointer_cast<B>(a)); };
}

void F(const funOfA f) {
    APtr a = std::make_shared<A>();
    APtr b = std::make_shared<B>();

    std::cout << "f(a): " << f(a) << "\n";
    std::cout << "f(b): " << f(b) << "\n";
}

int main() {
    F(f1);
    F(wrapFunOfB(f2));
}

代码减少了 25%。

【讨论】:

  • 像魅力一样工作,正是我所需要的。感谢您提供 C++03 版本,因为我坚持使用它并且无法使用 C++11(出于工作原因)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-05-03
  • 2023-03-26
  • 1970-01-01
  • 2017-07-18
  • 2016-02-19
  • 1970-01-01
  • 2020-03-16
相关资源
最近更新 更多