Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moved objects are still destructed?

While learning C++11, I was surprised by the way moved objects appear to behave. Consider this code:

#include <exception>
#include <iostream>
#include <type_traits>

class Moveable {
public:
  Moveable() {
    std::cout << "Acquire odd resource\n";
  }

  ~Moveable() noexcept(false) {
    std::cout << "Release odd resource\n";
    // if (!std::uncaught_exception() && error_during_release) {
    //   throw std::exception("error");
    // }
  }

  Moveable(Moveable const &) = delete;
  Moveable &operator=(Moveable const &) = delete;

  Moveable(Moveable &&) = default;
  Moveable &operator=(Moveable &&) = default;
};

int main(int argc, char *argv[]) {
  static_assert(!std::is_copy_constructible<Moveable>::value,
    "is not copy constructible");
  static_assert(!std::is_copy_assignable<Moveable>::value, "is not copy assignable");
  static_assert(std::is_move_constructible<Moveable>::value, "is move constructible");
  static_assert(std::is_move_assignable<Moveable>::value, "is move assignable");

  Moveable moveable{};
  Moveable moved{std::move(moveable)};
  Moveable moved_again{std::move(moved)};
}

It yields this output:

$ clang++ --version
clang version 3.8.0 (tags/RELEASE_380/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /opt/clang+llvm-3.8.0-x86_64-linux-gnu-ubuntu-14.04/bin
$ clang++ --std=c++14 --stdlib=libc++ -Wall -Werror -o move_and_destroy move_and_destroy.cc  && ./move_and_destroy
Acquire odd resource
Release odd resource
Release odd resource
Release odd resource

I'm surprised because I was hoping to create a movable RAII type. However it seems each moved intermediate is destructed!

Is there some variation of this that allows me to release my resource once at the end of my "object's lifetime"? (that is, at the end of the lifetimes of the sequence of moved objects?)

Someone in a similar situation should probably use std::unique_ptr and be done. However in this scenario it is possible for ~Moveable() to throw, and apparently std::unique_ptr's destructor will terminate the program on exception (at least, in clang 3.8.0.)

like image 715
phs Avatar asked Jul 10 '16 00:07

phs


People also ask

What should the state of a moved-from object be?

- The Old New Thing What should the state of a moved-from object be? In the C++ language, there is the concept of moving, which is a way of transferring resources from one object to another. The language specifies that a moved-from object is in a legal but indeterminate state.

What is a moved-from object in Java?

The language specifies that a moved-from object is in a legal but indeterminate state. Basically, the object is in a state that can be safely destructed, or operated upon in a way that is not dependent on the previous state (say, assigning a new value).

Are You a victim of disappearing object phenomenon?

You might be a victim of disappearing object phenomena (DOP). What could be the cause? Typically, DOP involves an object that the person had just been using or that they invariably keep in one particular place.

What happens when a static object goes out of scope?

This is because the object is now stack based object and it is destroyed when it is goes out of scope and its destructor will be called. How about global static objects? The following program demonstrates use of global static object.


2 Answers

Yes, moved-from objects are destructed. They remain in an undetermined but valid state. They're still objects.

It's best if you recall that C++ doesn't actually move anything. std::move just gives you an rvalue. So-called "move constructors" are just convenient alternatives to copy constructors, found during lookup when you have an rvalue, and allowing you the opportunity to swap your class's encapsulated data rather than actually copying it. But C++ doesn't move anything for you, and it can't tell when you have done some moving.

As such, it would be infeasibly dangerous and impractical for C++ to have any kind of rule that somehow stopped "moved-from" objects, if we could even decide what this meant in general, from later undergoing destruction. Make this destruction safe (a no-op, ideally) for your moved-from objects (e.g. by setting source pointers to nullptr in your move constructor) and you'll be fine.

like image 125
Lightness Races in Orbit Avatar answered Oct 09 '22 20:10

Lightness Races in Orbit


Yes, moved-from objects are still destructed. To correctly release the resource once, after all moves, we need to tell the destructor when the object has been moved from:

#include <exception>
#include <iostream>
#include <type_traits>

class Moveable {
private:
  bool moved_from;

public:
  Moveable() : moved_from(false) {
    std::cout << "Acquire odd resource\n";
  }

  ~Moveable() noexcept(false) {
    // We have already been moved from! Do nothing.
    if (moved_from) {
      std::cout << "Not releasing odd resource\n";
      return;
    }

    std::cout << "Release odd resource\n";
    // if (!std::uncaught_exception() && error_during_release) {
    //   throw std::exception("error");
    // }
  }

  Moveable(Moveable const &) = delete;
  Moveable &operator=(Moveable const &) = delete;

  Moveable(Moveable &&moveable) {
    moved_from = false;
    moveable.moved_from = true;
    // And now we spell out the explicit default move constructor
  }

  Moveable &operator=(Moveable &&moveable) {
    moved_from = false;
    moveable.moved_from = true;
    // And now we spell out the explicit default move assignment operator
    return *this;
  }
};

int main(int argc, char *argv[]) {
  static_assert(!std::is_copy_constructible<Moveable>::value,
    "is not copy constructible");
  static_assert(!std::is_copy_assignable<Moveable>::value, "is not copy assignable");
  static_assert(std::is_move_constructible<Moveable>::value, "is move constructible");
  static_assert(std::is_move_assignable<Moveable>::value, "is move assignable");

  Moveable moveable{};
  Moveable moved{std::move(moveable)};
  Moveable moved_again{std::move(moved)};
}

This yields

$ clang++ --std=c++14 --stdlib=libc++ -Wall -Werror -o move_and_destroy move_and_destroy.cc  && ./move_and_destroy
Acquire odd resource
Release odd resource
Not releasing odd resource
Not releasing odd resource
like image 43
phs Avatar answered Oct 09 '22 19:10

phs