Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compiler doesn't fail when pushing back a std::unique_ptr into a std::vector

An unique_ptr cannot be pushed back into a std::vector since it is non-copyable, unless std::move is used. However, let F be a function that returns a unique_ptr, then the operation std::vector::push_back(F()) is allowed. There is an example below:

#include <iostream>
#include <vector>
#include <memory>

class A {
  public:
    int f() { return _f + 10; }

  private:
    int _f = 20;
};

std::unique_ptr<A> create() { return std::unique_ptr<A>(new A); }


int main() {
  std::unique_ptr<A> p1(new A());

  std::vector< std::unique_ptr<A> > v;

  v.push_back(p1); // (1) This fails, should use std::move

  v.push_back(create()); // (2) This doesn't fail, should use std::move?

  return 0;
}

(2) is allowed, but (1) is not. Is this because the returned value is moved somehow implicitly?

In (2), is it actually necessary to use std::move?

like image 527
Dan Avatar asked Jul 07 '19 20:07

Dan


2 Answers

std::move(X) essentially means "here, treat X as if it was a temporary object".

create() returns a temporary std::unique_ptr<A> to begin with, so move is unnecessary.


If you want to know more, look into the value categories. Your compiler uses value categories to determine if an expression refers to a temporary object ("rvalue") or not ("lvalue").

p1 is an lvalue, and create() is an rvalue.

like image 192
HolyBlackCat Avatar answered Nov 19 '22 06:11

HolyBlackCat


std::vector::push_back() has an overload that takes an rvalue reference as input:

void push_back( T&& value );

The return value of create() is an unnamed temporary, ie an rvalue, so it can be passed as-is to push_back() without needing to use std::move() on it.

std::move() is needed only when passing a named variable, ie an lvalue, where an rvalue is expected.

like image 8
Remy Lebeau Avatar answered Nov 19 '22 08:11

Remy Lebeau