Почему деструктор вызывается перед конструктором перемещения при возврате временного (rvalue)

Я подозреваю, что не понимаю кое-что о семантике перемещения. Учитывая следующий код, я ожидал бы, что отладчик (MSVC2010SP1) будет вызывать членов Proxy в следующем порядке:

  • Proxy(Resource*) строительство временного в getProxy
  • Proxy(Proxy&& other) двигать конструирование p
  • ~Proxy() разрушая пустую оболочку временного, который получил его кишки, взятые ходом
  • ~Proxy() p выходит за рамки

    class Resource
    {
    void open(){}
    public:
    void close(){}
    Proxy && getProxy();
    };
    class Proxy
    {
    Resource *pResource_;
    Proxy(const Proxy& other); //disabled
    Proxy& operator=(const Proxy& other); //disabled
    public:
    Proxy(Resource *pResource):pResource_(pResource){}
    Proxy(Proxy&& other):pResource_(other.pResource_){other.pResource_ = nullptr;}
    ~Proxy()
    {
    if(pResource_)
    pResource_->close();
    pResource_ = nullptr;
    }
    };
    
    Proxy && Resource::getProxy()
    {
    open();
    return Proxy(this);
    }
    
    //somewhere else, lets say in main()
    Resource r;
    {
    auto p = r.getProxy();
    }   // p goes out of scope
    

Вместо этого порядок:

  • Proxy(Proxy*)
  • ~Proxy() // это уже вызывает close() раньше, чем ожидалось
  • Proxy(Proxy&& other) // движение после разрушения дает p.pResource_ значение nullptr
  • ~Proxy() //p выходит за рамки

Это не имеет смысла для меня. Я пытаюсь отследить время жизни прокси-класса, передающего задание на закрытие ресурса через конструктор перемещения от одного объекта к другому.

2

Решение

getProxy() возвращает ссылку на временный объект, который выходит из области видимости в конце функции и приводит к зависанию ссылки.

4

Другие решения

Возврат по rvalue-ссылке на самом деле не приводит к перемещению чего-либо. Он просто возвращается по ссылке. Однако это отличается от возврата ссылки на lvalue, потому что выражение, вызывающее функцию, которая возвращает ссылку на rvalue, является xvalue (в отличие от lvalue). Затем xvalue (как подмножество выражений rvalue) может быть перемещен из. Если вы хотите перейти от возвращенного объекта функции, возвращающей ссылку на lvalue, вам придется использовать std::move чтобы сделать это ценным.

Вы очень редко захотите вернуть ссылку на значение. Единственное расплывчатое использование — разрешить перемещение частного члена объекта. Если вы хотите, чтобы объект был перемещен при возврате из функции, просто верните его по значению. В вашем случае, если тип возврата getProxy Было просто Proxy, временный объект будет перемещен из возвращаемого объекта, а затем он будет перемещен из в p (за исключением любого выбора).

Как у вас есть, ваш временный объект Proxy(this)) уничтожается в конце return Заявление — это первый вызов деструктора. Возвращенная ссылка теперь ссылается на недопустимый объект и p построен путем перемещения из этой недействительной ссылки. Это дает вам неопределенное поведение.

2

По вопросам рекламы [email protected]