我开发了一些可以正确编译的代码,但是在(调试)运行时失败。我正在使用VS2015。

背景:我正在构建一个高级消息引擎。为了使新消息的编程添加保持可维护性,在生产代码中,我花了一些时间使用explicit initialization declaration C++结构编写初始消息。这行之有效,并使新消息的制作变得千篇一律,更不用说将消息传递胆量的维护几乎减少了。这是此功能的基本代码:

#include <memory>

template< typename D_T >
struct H // prototype for all explicit initialization declarations (EID)
{
  H( D_T& d ) : x { d } {}
  D_T& x;
};

template< typename D_T >
struct B  // base class for derived objects D1 and D2
{
  B( D_T& d ) : d { d } {}
  D_T& d; // a kind of backptr initialized when the EIDs are contructed

  // actual EIDs a and b
  H< D_T > a { d };
  H< D_T > b { d };
};

struct D1 : public B< D1 >
{
  D1() : B( *this ) {}
  void Func1() {}
};

struct D2 : public B< D2 >
{
  D2() : B( *this ) {}
  void Func2() {}
};



int main()
{
  D1 d1;
  D2 d2;

  // as designed either derived object can access either explicitly initialized member a or b
  d1.a.x.Func1(); // OK
  d1.b.x.Func1(); // OK
  d2.a.x.Func2(); // OK
  d2.b.x.Func2(); // OK

  return 0;
}

此代码编译并运行。

但是我在真实代码中的派生对象是共享的ptrs。因此,我将此功能添加到了代码中。请注意,我正在使用this构造获取派生类的enable_shared_from_this ptr:
#include <memory>

template< typename D_T >
struct H
{
  H( std::shared_ptr< D_T >& d ) : x { d } {}
  std::shared_ptr< D_T >& x;
};

template< typename D_T >
struct B
{
  B( std::shared_ptr< D_T >& d ) : d { d } {}
  std::shared_ptr< D_T >& d;

  H< D_T > a { d }; // a is initialized with D1
  H< D_T > b { d };
};

struct D1: public std::enable_shared_from_this< D1 >, public B< D1 >
{
  D1() : B( shared_from_this() ) {} // runtime error: bad weak prt
  void Func1() {}
};

struct D2: public std::enable_shared_from_this< D2 >, public B< D2 >
{
  D2() : B( shared_from_this() ) {}
  void Func2() {}
};

int main()
{
  D1 d1;
  D2 d2;

  d1.a.x->Func1();
  d1.b.x->Func1();
  d2.a.x->Func2();
  d2.b.x->Func2();

  return 0;
}

这段代码会编译。但是,它没有运行,并且在D1构造函数中,它以异常std::bad_weak_ptr中断。

我试图将共享ptrs更改为弱ptrs,但没有成功。有人看到问题了吗?

编辑1:
@pat观察到无法从构造函数中调用shared_from_this(),请参阅下面的修改后的代码,该代码现在可以编译并运行:
#include <memory>

template< typename D_T >
struct H
{
  H( D_T& d ) : x { d } {}
  D_T& x;
};

template< typename D_T >
struct B
{
  B( D_T& d ) : d { d } {}
  D_T& d;

  H< D_T > a { d };
  H< D_T > b { d };
};

struct D1 : public std::enable_shared_from_this< D1 >, public B< D1 >
{
  D1() : B( *this ) {}
  void Func1() {}
};

struct D2 : public std::enable_shared_from_this< D1 >, public B< D2 >
{
  D2() : B( *this ) {}
  void Func2() {}
};

int main()
{
  D1 d1;
  D2 d2;

  d1.a.x.Func1();
  d1.b.x.Func1();
  d2.a.x.Func2();
  d2.b.x.Func2();

  return 0;
}

编辑2:
下面的代码是对我原始帖子代码的重写,并以@pat的答案为基础。更改内容如下:显式实例化声明(EID)已移至其派生类。 B不再尝试引用派生对象。这是一个明显的错误。作为后向指针的weak_ptr被简单的后向ptr代替(原型(prototype)中就是这种情况)。由于派生对象(D1和D2)完全拥有该对象,因此泄漏没有问题。 (在生产代码中,成员类型是共享的ptrs,以防止泄漏。)
#include <memory>
#include <cassert>

template< typename D_T >
struct H
{
  H( D_T* d ) : x { d } {}
  D_T* x;
  int qq { 0 };
};

struct B
{
  B() {}
  int rr { 0 };
};

struct D1 : public B
{
  H< D1 > a { this }; // explicit instantiation declaration
  int ss { 0 };
};

struct D2 : public B
{
  H< D2 > b { this }; // explicit instantiation declaration
  int tt { 0 };
};


int main()
{
  D1 d1;
  D2 d2;
  d1.rr = 99;
  d2.b.x->rr = 88;
  assert( d1.rr == d1.a.x->rr ); // OK
  assert( d2.rr == d2.b.x->rr ); // OK

  return 0;
}

当添加任意数量的EID时,代码的维护复杂度从指数(如原型(prototype)中的情况)降低为线性的设计不变性已经实现。

最佳答案

该对象必须由共享指针管理,以便shared_from_this起作用。在C++ 14中,实际上尚未定义的行为是在尚未由shared_from_this管理的对象上调用shared_ptr。因此,您将无法从构造函数中调用shared_from_this,因为此时该对象将不在shared_ptr中。

来自cppreference的示例...

struct Good: std::enable_shared_from_this<Good>
{
    std::shared_ptr<Good> getptr() {
        return shared_from_this();
    }
};
// Bad: shared_from_this is called without having std::shared_ptr owning the caller
try {
    Good not_so_good;
    std::shared_ptr<Good> gp1 = not_so_good.getptr();
} catch(std::bad_weak_ptr& e) {
    // undefined behavior (until C++17) and std::bad_weak_ptr thrown (since C++17)
    std::cout << e.what() << '\n';
}

09-06 22:48