I have a segment similar to the following.
struct derive : base{
derive(unique_ptr ptr): base{func(ptr->some_data), std::move(ptr)}{}
};
In theory, it should work. But since the compiler (vs2015) does not strictly follow the standard, the order of func(ptr->some_data), std::move(ptr)
is undefined, ie ptr
may be moved before accessed.
So my problem is how to make this segment work as expected?
Complete code like this:
#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) } {}
};
You can fix it using constructor chaining:
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)) {}
};
Reason: As explained in my other answer, the call to func
definitely takes place before the delegated constructor call, while actually moving the unique_ptr
(as opposed to merely changing its value category) definitely takes place inside.
Of course, this relies on another C++11 feature which Visual C++ may or may not have gotten right. Happily, delegating constructors are listed as supported since VS2013 .
An even better thing to do is just always accept std::unique_ptr
arguments by reference, and by rvalue reference if you plan to steal from them . (And if you won't steal the content, why do you care what type of smart pointer the caller has? Just accept a raw T*
.)
If you used
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) } {}
};
you wouldn't have had the problem in the first place, and the caller requirements are completely unchanged (an rvalue must be provided, since unique_ptr
is uncopyable anyway)
The rationale for making this a universal rule is as follows: pass by value allows either copying or moving, whichever is more optimal at the call site. But std::unique_ptr
is non-copyable, so the actual parameter MUST be an rvalue anyway.
The order is indeed undefined, but that doesn't matter because std::move
doesn't actually move from the pointer, it only changes the value category.
The call to
func(ptr->some_data)
will take place before the pointer is moved, because the first is argument evaluation and the latter happens inside the base constructor, and argument evaluation always is ordered before a function call.
If it makes you feel better, you can write it as the 100% equivalent:
derive(unique_ptr<X> ptr): base{func(ptr->some_data), (unique_ptr<X>&&)ptr}{}
Edit: the actual move doesn't take place inside the called function, if the parameter is pass by-value. But who does such a thing with unique_ptr
s?
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.