Ответ 1
Вы можете исправить это, используя цепочку конструкторов:
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
(а не просто изменение его категории значений) определенно происходит внутри.
Конечно, это зависит от другой возможности С++ 11, которую Visual С++ может или не может получить. К счастью, делегирующие конструкторы перечислены как поддерживаемые с VS2013.
Еще лучше сделать только всегда принимать аргументы std::unique_ptr
по ссылке и по ссылке rvalue, если вы планируете украсть у них. (И если вы не будете красть контент, почему вам небезразличен тип смарт-указателя, который имеет вызывающий абонент? Просто примите raw 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) } {}
};
у вас не было бы проблемы в первую очередь, и требования к вызывающему абоненту полностью не изменились (должно быть предоставлено rvalue, так как unique_ptr
в любом случае не подлежит сомнению)
Обоснование принятия этого универсального правила заключается в следующем: pass by value позволяет либо копировать, либо перемещаться, в зависимости от того, что более оптимально на сайте вызова. Но std::unique_ptr
не скопируется, поэтому фактический параметр ДОЛЖЕН быть rvalue в любом случае.