0

In Rust, non-primitive types are not copied, but are moved by default. This is great, because they're expensive to copy.

struct Post { title: String, content: String };
let (title, content) = ("some title".to_string(), "very long post...".to_string()); // imagine they're from the user
let post = Post { title, content };
// can longer access `title` and `content`

If you really want a copy, it's simple: just clone it (although in 99% of the cases you don't actually need to copy, and clone() signifies an anti-pattern):

let post = Post { title: title.clone(), content: content.clone() };

If you write similar code in C++:

class Post {
    string title, content;
public:
    Post(string t, string c) : title{ t }, content{ c }{};
};


int main() {
    string title = "some title";
    string content = "very long post...";
    Post post(title, content);
}

The strings get copied.

I know there is std::move, but the ergonomics isn't very good, as demonstrated in this post. The author also suggested using shared_ptr, but that would cause a quite large overhead (about the same as Rust's Arc).

So the questions are, is it possible to mimic Rust's move semantics in C++? Is it idiomatic to do that? If not, what are the commonly used practices to prevent copying non-primitive objects in C++?

Tianyi Shi
  • 883
  • 10
  • 15
  • You could do what every C++ library since the dawn of time has done: write your own `std::string` that has the behavior you want (namely, deleted copy constructor). Or you could find a linter that flags implicit copies. – Silvio Mayolo Nov 19 '20 at 17:24
  • The question here isn't really clear. Particularly the answer to "how do C++ developers do X" is - depends on the specific needs of the specific C++ developer. Perhaps delete or make private the copy constructor/assignment operator. If you also want to still be able to make copies explicitly then add a method `Post Post::clone() const`. Perhaps the issue is that C++ move semantics provide no information to the compiler that the object has been moved from, and thus a moved-from object is still lexically accessible, and the programmer can shoot themselves in the foot by re-using it? – user2407038 Nov 19 '20 at 17:37
  • 1
    "`clone()` signifies an anti-pattern". This is not true. There are times where cloning is the idiomatic solution to a problem. – Ibraheem Ahmed Nov 19 '20 at 17:49
  • Not technically a dupe, but have a look at https://stackoverflow.com/questions/18673658/should-i-always-move-on-sink-constructor-or-setter-arguments. – Stephen Newell Nov 19 '20 at 21:06

1 Answers1

3

There are two ways in C++ to prevent implicit copying.

As the type author, you can delete the copy constructor and copy assignment operator. This disallows copying even when the copy is explicit. It is heavy-handed.

As the type consumer, you can take an rvalue reference:

void foo(T&& t)

If foo is passed a temporary, an implicit move happens. If a copy is wanted, one can make an explicit copy either naming the type or using something like the copy function below. If someone wants to pass a non-temporary, both move and copy must be explicit.

// one way to make a copy explicit
T copy(const T& t) { return t; }
foo(copy(get_t()));
Jeff Garrett
  • 5,863
  • 1
  • 13
  • 12