2016-03-09 4 views
0

Позволяет рассмотрят следующий фрагмент кода:Скользящей семантика unique_ptr

template<typename T> 
void f(std::unique_ptr<T>&& uptr) { /*...*/ } 

В другой функции:

void g() 
{ 
    std::unique_ptr<ANY_TYPE> u_ptr = std::make_unique<ANY_TYPE>(); 
    f(std::move(u_ptr)); 
X: u_ptr->do_sth(); // it works, I don't understand why. Details below. 
} 

Я не понимаю, почему u_ptr в линии X все еще жив. В конце концов, я заставил его двигаться (std :: move).

---EDIT--- 
Ok, so now: 
The code is still working: 

class T{ 
    public: 
    T(){} 

    void show(){ 
     std::cout << "HEJ!\n"; 
    } 
}; 

void f(std::unique_ptr<T> ref){ 
    ref->show(); 
} 

int main() 
{ 
    std::unique_ptr<T> my; 
    my->show(); 
    f(std::move(my)); 
    my->show(); // How is it possible. Now, f takes unique_ptr by value 


    return 0; 
} 

ответ

1

Вы не показали нам, что код функционировать f, но предположительно это не перемещать указатель, даже если он имел разрешение.

Вы передали unique_ptr по ссылке. Если вызов функции действительно переместил его, то функция не могла его использовать, потому что она исчезла до того, как функция имела шанс.

Если вы хотите вызвать функцию для фактического перемещения указателя, вам нужно передать указатель по значению, а не ссылку. Это значение будет unique_ptr для его перемещения. В этом случае вы должны объявить эту функцию как взятие std::unique_ptr<T> вместо std::unique_ptr<T>&&. Затем вы можете вызвать конструктор перемещения при вызове функции.

Обновление: с вашим последним изменением unique_ptr больше не будет ссылаться на какой-либо действительный объект из-за конструкции перемещения. Вы просто никогда не проверяете это. Вызов не виртуального метода, который не имеет доступа к каким-либо переменным-членам, может работать одинаково независимо от того, действителен или уничтожен объект, потому что ему ничего не нужно от объекта. Вы также никогда не делали на самом деле unique_ptr.

Вместо этого наведите указатель на пункт unique_ptr. После его перемещения попробуйте вызвать виртуальную функцию или получить доступ к члену, значение которого изменяется деструктором. Как это:

#include <iostream> 
#include <memory> 

class T{ 
    public: 
    T() : valid (true) {} 
    ~T() { valid = false; } 

    bool valid; 

    void show(){ 
     std::cout << "HEJ! " << valid << std::endl; 
    } 
}; 

void f(std::unique_ptr<T> ref){ 
    ref->show(); 
} 

int main() 
{ 
    std::unique_ptr<T> my (new T); // Make it point to a new object 
    my->show(); 
    f(std::move(my)); 
    my->show(); // Try to access 


    return 0; 
} 
+0

Но почему он не двигал указателем? Должен ли он называться move_constructors для функции вызова? – Gilgamesz

+0

Вы передали 'unique_ptr' по ссылке. Переместить конструкцию нечего. Если вы хотите вызвать конструктор перемещения, вам нужно передать значение 'unique_ptr' по значению, а не по ссылке. Затем вы можете перемещать конструкцию этого значения. –

+2

* Теперь * ваш код имеет неопределенное поведение. Попробуйте вызвать виртуальную функцию или получить доступ к члену, значение которого изменяется деструктором. (Вы делаете это дважды, так как 'my' никогда не ссылается на' T'.) –

1

Причина, почему ваш призыв к show не врезаться происходит потому, что он не использует this указателя (он не пытается изменить или доступ к элементу данных).

Попробуйте это:

class T{ 
    public: 
    int v; 
    T(){} 

    void show(){ 
     v = 0; 
     std::cout << "HEJ!\n"; 
    } 
}; 

недействительным п (станд :: unique_ptr & & реф)

Это ответ, когда вы первоначально имели свою f функцию принимая ссылку RValue & &.

Ваша функция принимает значение с ссылкой. Поэтому новый объект unique_ptr еще не создан, вы просто проходите мимо reference.

Внутри вашей f функции, если вы создаете локальную unique_ptr с параметром uptr, то в конце концов uptr будут перемещены, чтобы создать этот новый объект.

template<typename T> 
void f(std::unique_ptr<T>&& uptr) 
{ 
    //move uptr into local_unique_ptr 
    //note that we have to use move again 
    //because uptr has a name, therefore its a lvalue. 
    auto local_unique_ptr = std::unique_ptr<T>(std::move(uptr)); 
} 

Важно, чтобы всегда знать, что std::move просто static_cast.

Если вы передадите lvalue по номеру std::move, он возвращает rvalue. Если вы передадите rvalue, он возвращает rvalue. Вот и все.

+0

Это потому, что ваша функция show не имеет доступа или не изменяет элемент данных (поэтому никогда не пытается использовать этот указатель), это похоже на вызов статической функции. Если вы измените функцию show, чтобы выполнить изменение члена данных, он сработает. – Jts

+0

Хорошо, но unique_ptr.get() должен быть нулевым ptr, так как можно вызвать эту функцию (показать). Возможно, нам нужно знать адрес объекта (это) и на самом деле является нулевым указателем. – Gilgamesz

+0

Это nullptr. Я изменил свой ответ, чтобы сбой вашей программы. Просто вызов функции с недопустимым указателем не приводит к сбою, если только эта функция не использует этот указатель. – Jts

0

Возможно, ваша функция f не может перемещать указатель. Простое удаление объекта на && не изменяет объект.

u_ptr->do_sth() может вызывать статическую функцию-член или функцию-член, которая не имеет доступа к объекту (this), и поэтому она не сбой.

+0

* "(...) и вот почему он не терпит крах." * Хотя это все еще UB –

+0

@PiotrSkotnicki Проницательный комментарий. Есть печенье. –

+0

@PiotrSkotnicki Но подождите! Это поведение _undefined_ или поведение _unspecified_? Это важно здесь! –

1

в строке f(std::unique_ptr<T>&& uptr)uptr не является объектом - это ссылка. ссылку, которая способна захватывать временные события и мутировать их.

это как спрашивать, почему не объект Клонирование в следующем примере

void func(std::string& str); 
std::string str_ = "yyy"; 
func(str_); 

str_ передается по «обычной» ссылки и не копируются - это то, что проходит по справочным средством.

std::move только ссылается на значение r-значение-значение, которое uptr в f(std::unique_ptr<T>&& uptr) может ссылаться, это ссылка, ссылающаяся на объект. в противоположность общей концепции, std::move не будет делать никаких движений сам по себе, только бросает объект в r-value-reference для конструктора move/assg. оператор, который должен нажать.

здесь указатель по-прежнему хранит действительные данные, так как он не был перемещен, только отбрасывается в r-value-reference.

, если вы хотите, чтобы объект, чтобы переместить вы должны объявить параметр как объект, а не ссылаться: f(std::unique_ptr<T> uptr)

В вашем редактировании, вы undefiend поведения, так что все может больше появляться.