You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过std::ref向std::bind传递shared_ptr会丢失多态性?

std::ref与std::bind结合导致dynamic_cast触发SIGSEGV崩溃的原因分析

问题描述

我在尝试将Base类的this指针通过dynamic_cast转换为Derived类指针时触发了SIGSEGV崩溃,怀疑该问题与std::bind和std::ref的使用有关。当注释掉使用std::ref的版本、改用无引用版本时,程序可正常运行无崩溃。疑惑为何std::ref会导致这种情况,难道是Base类shared_ptr的多态性被破坏了?

可复现代码

头文件 h.hpp

#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <vector>

using namespace std;

template <typename T> 
class Derived;

class Base {
public:
  virtual ~Base() {}

  template <typename T> 
  void foo(const T &t) {
    cout << "Base foo\n";
    auto derived = dynamic_cast<Derived<T> *>(this); // SIGSEGV crash!
    derived->foo(t);
  }

private:
};

template <typename T> 
class Derived : public Base {
public:
  void foo(const T &t) { cout << "Derived foo\n";
  }

private:
};

class Outter {
public:
  void init();
  void run();

private:
  void run_foo(shared_ptr<Base> &base);

  class Inner {
  public:
    void run() {
      for (const auto &f : foos_) {
        f();
      }
    }

    void set_foo(function<void()> f) { 
        foos_.push_back(f); 
    }

  private:
    static vector<function<void()>> foos_;
  };

  Inner inner_;
  vector<shared_ptr<Base>> bases_;
};

源文件 main.cpp

#include "h.hpp"

vector<function<void()>> Outter::Inner::foos_;

void Outter::init() {
  shared_ptr<Base> base = make_shared<Derived<int>>();
  bases_.push_back(base);
  
//   inner_.set_foo(bind(&Outter::run_foo, this, base)); // no ref version
  inner_.set_foo(bind(&Outter::run_foo, this, ref(base))); // ref verison
}

void Outter::run() { inner_.run(); }

void Outter::run_foo(shared_ptr<Base> &base) {
  int t = 123;
  base->foo(t);
}

int main() {
  Outter outter;
  outter.init();
  outter.run();
  return 0;
}

原因分析与解决方案

这根本不是多态性被破坏的问题,而是引用了已经销毁的局部变量导致的未定义行为:

  • 在Outter::init()函数中,shared_ptr<Base> base是一个局部变量,当init()执行完毕后,这个局部变量的生命周期就结束了。
  • 当你用std::ref(base)配合std::bind时,bind存储的是这个局部shared_ptr的引用,而不是拷贝。当后续inner_.run()调用绑定的函数时,引用指向的局部变量已经被销毁,此时base引用的是一个无效的内存地址。
  • 后续对base->foo(t)的调用本质是操作一个已经失效的shared_ptr,这会导致未定义行为,包括SIGSEGV崩溃,此时dynamic_cast的崩溃只是这种未定义行为的表现之一。

而无引用版本中,std::bind会拷贝shared_ptr<Base> base,这个拷贝的shared_ptr会和bases_中的元素共享同一个对象的所有权,保证了对象的生命周期足够长,因此程序可以正常运行。

修复方案

  • 去掉std::ref,让bind拷贝shared_ptr(即测试过的正常版本);
  • 如果一定要传递引用,需要确保被引用的shared_ptr生命周期长于绑定函数的调用时机,比如绑定bases_中元素的引用(ref(bases_.back())),因为bases_是Outter的成员变量,生命周期和Outter实例一致。

内容的提问来源于stack exchange,提问作者Komgcn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 01:52:50