在将其移入基类构造函数之前,如何从unique_ptr中提取原始指针

我非常需要访问特定于派生类的功能,在构造包含类时,我在unique_ptr中获得了一个实例。然后,此包含类必须将unique_ptr转换到其基类,再移到包含类的基类构造函数中,并在其中最终move拥有该基类的所有权。一些代码应该会有所帮助:

class MemberBase {};
class MemberDerived : public MemberBase { /*some public stuff not in MemberBase*/ };

class MainBase {
  std::unique_ptr<MemberBase> member_;

 public:
  MainBase(std::unique_ptr<MemberBase> member) : member_(std::move(member)) {}
};

class MainDerived : public MainBase {
  MemberDerived* member_derived_;
  // This class proceeds to use MemberDerived-only functions

 public:
  // How to write the initialization list below?
  MainDerived(std::unique_ptr<MemberDerived> member)
    : MainBase(std::move(member)),member_derived_(member.get() /*nullptr!!*/) {}
};

忽略整体类设计的问题(我知道耦合到MemberDerived中特定于MainDerived的特定函数并不理想;这是继承的代码,如果不进行重大重构就无法更改),在转发unique_ptrMainBase之前,我如何获取原始指针?

在下面找到我已经想到的一些想法,以及为什么我认为这些想法不好。

  1. 受保护的访问者下传:
// Add this method to the protected section of MainBase:
MemberBase* MainBase::get_member() { return member_.get(); }

// Then downcast in MainDerived's c'tor
MainDerived::MainDerived(std::unique_ptr<MemberDerived> member)
  : MainBase(std::move(member)),member_derived_(dynamic_cast<MemberDerived*>(get_member())) {}

这应该可以,但是使用dynamic_cast(本身的主要缺点),当然,如果有人将传递给c'tor的类型更改为不是从MemberDerived派生的类型,它将不会中断编译器的帮助。

  1. 两次传入指针:
// member and member_derived must point to the same object!
MainDerived::MainDerived(std::unique_ptr<MemberDerived> member,MemberDerived* member_derived)
  : MainBase(std::move(member)),member_derived_(member_derived) {}

除了创建一个非常丑陋的c'tor签名外,用户在调用move之前为两个参数传递不同的指针或执行get也非常容易。此外,现在用户被迫创建一个局部变量以将其传递到两个地方。我说过丑吗?

  1. 使用辅助功能欺骗性翻转初始化顺序:
template <typename T>
std::unique_ptr<T> ExtractPointer(std::unique_ptr<T> p,T** target) {
  *target = p.get();
  return std::move(p);
}

MainDerived::MainDerived(std::unique_ptr<MemberDerived> member)
  : MainBase(ExtractPointer(std::move(member),&member_derived_)) {}

现在,我实际上感到有些惊讶,因为它没有产生任何警告/错误(带有-Wall的gcc 5.4.0)。我的部分人喜欢这样做,因为它很难破解,但是member_derived_的circuit回初始化给我带来了些许寒意。

yegenting2 回答:在将其移入基类构造函数之前,如何从unique_ptr中提取原始指针

您的第二个解决方案已经到了一半。请注意,构造函数不必为public。您可以将2个参数组成一个private,然后编写一个1参数public的构造函数来委托它。

class MainDerived : public MainBase {
  MemberDerived* member_derived_;

  MainDerived(std::unique_ptr<MemberDerived> &member,MemberDerived *member_derived)
    : MainBase(std::move(member)),member_derived_(member_derived) { }

 public:
  MainDerived(std::unique_ptr<MemberDerived> member)
    : MainDerived(member,member.get()) { }
};

member必须在private构造函数中作为引用,因为未指定函数调用的参数的求值顺序。因此,您无法安全地将member的{​​{1}}从public的构造函数移到private的参数中,因为移动和get()的顺序将不确定,但我们需要get()在移动之前发生。最简单的解决方法是让private构造函数通过引用获取member,而不会对其进行突变。 (您也可以使用{}而不是()来强制执行求值顺序,但是使代码依赖于如此微妙的结构并不是一个好主意。)

,

由于您知道指针指向MemberDerived,所以不需要dynamic_caststatic_cast即可。

如果您担心在更改某些内容时会在类型上犯错误,则只需引用元素类型:

MainDerived(std::unique_ptr<MemberDerived> member)
  : MainBase(std::move(member)),member_derived_(static_cast<decltype(member)::element_type*>(member_.get())) 
{}

正如您提到的,这要求member_protected或存在protected的吸气剂。

据我所知,在任何情况下都不会导致未定义的行为,除非有人传递的std::unique_ptr并未真正指向与{{ 1}},在这种情况下,使用此element_type本身将是不安全的,因为如果实例未经事先移动就被破坏,它将导致不确定的行为。


关于您3.建议的方法:

我认为这在C ++ 17中是技术允许的,因为std::unique_ptr具有虚假的初始化,因此其生存期始于分配存储空间,而不是初始化完成。

但是当前的C ++ 20草案删除了此异常,因此member_derived_的生存期仅在其空虚初始化之后才开始,从而使其使用未定义的行为。

本文链接:https://www.f2er.com/2945029.html

大家都在问