我有一段类似于以下的内容。
struct derive : base{
derive(unique_ptr ptr): base{func(ptr->some_data), std::move(ptr)}{}
};
从理论上讲,它应该有效。但由于编译器(vs2015)并未严格遵循该标准,因此func(ptr->some_data), std::move(ptr)
的顺序未定义,即ptr
可能会在访问之前被移动。
所以我的问题是如何使这个细分市场按预期工作?
完整的代码如下:
#include <memory>
struct base {
virtual ~base() = 0 {}
protected:
base(std::unique_ptr<base> new_state) :
previous_state{ std::move(new_state) } {}
private:
std::unique_ptr<base> previous_state;
};
struct derive_base : base {
int get_a() const noexcept {
return a;
}
protected:
derive_base(int const new_a, std::unique_ptr<base> new_state) :
base{ std::move(new_state) }, a{ new_a } {}
private:
int a;
};
struct final_state : derive_base {
final_state(std::unique_ptr<base> new_state) :
derive_base{ dynamic_cast<derive_base&>(*new_state).get_a(), std::move(new_state) } {}
};
答案 0 :(得分:5)
您可以使用构造函数链接修复它:
struct derive : base
{
private:
derive(const D& some_data, unique_ptr<X>&& ptr) : base{some_data, std::move(ptr)} {}
public:
derive(unique_ptr<X> ptr): derive(func(ptr->some_data), std::move(ptr)) {}
};
原因:正如我在其他答案中所解释的那样,对func
的调用肯定发生在委托构造函数调用之前,而实际移动unique_ptr
(而不仅仅是更改其值类别)肯定需要放在里面。
当然,这依赖于另一个C ++ 11功能,Visual C ++可能会或可能没有正确使用。令人高兴的是,delegating constructors are listed as supported since VS2013。
更好的办法是始终通过引用接受std::unique_ptr
参数,如果您计划从他们那里窃取,则使用右值参考。 (如果您没有窃取内容,为什么要关心调用者具有哪种类型的智能指针?只需接受原始T*
。)
如果您使用
struct base
{
virtual ~base() = 0 {}
protected:
base(std::unique_ptr<base>&& new_state) :
previous_state{ std::move(new_state) } {}
private:
std::unique_ptr<base> previous_state;
};
struct derive_base : base
{
int get_a() const noexcept {
return a;
}
protected:
derive_base(int const new_a, std::unique_ptr<base>&& new_state) :
base{ std::move(new_state) }, a{ new_a } {}
private:
int a;
};
struct final_state : derive_base
{
final_state(std::unique_ptr<base>&& new_state) :
derive_base{ dynamic_cast<derive_base&>(*new_state).get_a(), std::move(new_state) } {}
};
您首先不会遇到问题,并且调用者要求完全不变(必须提供右值,因为unique_ptr
无法复制)
使其成为通用规则的基本原理如下:按值传递允许复制或移动,无论哪个在呼叫站点更优化。但是std::unique_ptr
是不可复制的,所以实际参数必须是一个右值。
答案 1 :(得分:1)
顺序确实是未定义的,但这并不重要,因为std::move
实际上并没有从指针移动,它只会更改值类别。
对 func(ptr->some_data)
的调用将在指针移动之前进行,因为第一个是参数评估,后者发生在基础构造函数中,参数评估总是在函数调用之前排序。
如果它让你感觉更好,你可以把它写成100%的等价物:
derive(unique_ptr<X> ptr): base{func(ptr->some_data), (unique_ptr<X>&&)ptr}{}
击> <击> 撞击>
编辑:如果参数是按值传递,则实际移动不会在被调用函数内发生。但谁与unique_ptr
s做了这样的事情?