With reference to the following code, I do not understand why the move constructor is called both with an lvalue and rvalue. I would expect copy ctor to be printed when i pass an lvalue to the push method.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class my_obj
{
public:
my_obj()
{
cout << "default ctor\n";
}
my_obj(const my_obj& other)
{
cout << "copy ctor\n";
}
my_obj(my_obj&& other)
{
cout << "move ctor\n";
}
};
class test
{
public:
template<typename T>
void push(T&& object)
{
print(forward<T>(object));
}
template<typename T>
void print(T&& a)
{
cout << "move\n";
my_obj temp = forward<T>(a);
}
template<typename T>
void print(T& a)
{
cout << "val\n";
my_obj temp = forward<T>(a);
}
};
int main()
{
my_obj obj;
test f;
f.push(obj); // why is move ctor called here? shouldnt it be copy ctor since not rvalue
cout << "\nPUSHING TEMP\n\n";
f.push(my_obj {});
}
output:
default ctor
val
move ctor
PUSHING TEMP
default ctor
move
move ctor
In here:
template<typename T>
void print(T& a)
{
cout << "val\n";
my_obj temp = forward<T>(a);
}
a isn't a forwarding reference, it's an lvalue reference. The type T isn't a reference type. So forward<T>(a) behaves the same as move(a). The special case for forwarding references is that the template parameter deduces to a reference type, and forwarding with a reference type yields an lvalue.
You just want my_obj temp = a;
The move constructor is called because you are using std::forward without a forwarding reference (don't do that please). Basically, this line is the culprit:
my_obj temp = forward<T>(a);
Here, T is a my_obj. Remember that std::forward is just a glorified cast. In your case, it is equivalent to:
my_obj temp = static_cast<my_obj&&>(a);
So you are casting a to a rvalue reference, and an rvalue has move semantics, so you see the move constructor being called. You are basically getting the behavior of std::move.
Related
#include <memory>
#include <iostream>
class Token { public:
Token() { std::cout << "Token()"; }
~Token() { std::cout << "~Token()"; }
};
template <class T>
std::unique_ptr<T> foo(T t0) {
return std::unique_ptr<T>(new T(t0)); };
int main() {
Token&& t = Token();
auto ptr = foo<Token>(t);
return 0;
}
On which occasions will the destructor be called?
I think it will be called firstly when we call Token(), it creates a temporary object which is destructed immediately, then in the foo() function when t0 is destructed, and then when main() ends, and ptr goes out of scope.
But my friend says otherwise, so how will it actually be?
When a scope ends and the automatic objects of that scope are destroyed, their destructors will be called in the reverse order the objects were created:
int main() {
Token&& t = Token(); // Token1 constructed
// lifetime of Token1 extended because it was bound to t
auto ptr = foo<Token>(t); // creates a copy of Token1 for the argument of foo: Token2
// Token3 constructed by foo in dynamic memory
// and bound to ptr, which
// resides in automatic memory
// Token2 (temporary copy) is automatically destroyed
return 0;
// Last automatic object is destroyed: ptr
// thus, uniqe_ptr destroys Token3
// t is destroyed. This destroys Token1 because
// its lifetime-extending reference
// went out of scope
}
Demo
If you modify your Token class slightly you can observe this live:
class Token {
inline static int C = 1;
int c = C++;
public:
Token(Token const&) : Token() {}
Token(Token&&) : Token() {}
Token() { std::cout << "Token(" << c << ")\n"; }
~Token() { std::cout << "~Token(" << c << ")\n"; }
};
Output:
Token(1)
Token(2)
Token(3)
~Token(2)
~Token(3)
~Token(1)
(live demo)
#bitmask's explanation about life-time is pretty clear. Besides, if you want to get the best performance, maybe you could implement the move constructor for your class Token, and then:
template <class T>
std::unique_ptr<T> foo(T& t0) { // pass-by-reference
return std::make_unique<T>(std::move(t0));
};
As you have used rvalue reference Token&& t to extend the temporary Token object's lifetime in function int main() and the rvalue reference t is a lvalue, so auto foo(T &) will deduce T as class Token and accept t as its parameter t0 according to Overload resolution. Furthermore, std::move cast lvalue t0 to rvalue reference and std::make_unique call the constructor which satisfies std::is_nothrow_constructable_v<Token,Token&&> is true, the move constructor and copy constructor are both candicate functions. If you have implemented Token(Token&&), move constructor will be called. Otherwise is copy constructor.
In the following code, it seems that the compiler sometimes prefer to call the templated constructor and fails to compile when a copy constructor should be just fine. The behavior seems to change depending on whether the value is captured as [v] or [v = v], I thought those should be exactly the same thing. What am I missing?
I'm using gcc 11.2.0 and compiling it with "g++ file.cpp -std=C++17"
#include <functional>
#include <iostream>
#include <string>
using namespace std;
template <class T>
struct record {
explicit record(const T& v) : value(v) {}
record(const record& other) = default;
record(record&& other) = default;
template <class U>
record(U&& v) : value(forward<U>(v)) {} // Removing out this constructor fixes print1
string value;
};
void call(const std::function<void()>& func) { func(); }
void print1(const record<string>& v) {
call([v]() { cout << v.value << endl; }); // This does not compile, why?
}
void print2(const record<string>& v) {
call([v = v]() { cout << v.value << endl; }); // this compiles fine
}
int main() {
record<string> v("yo");
print1(v);
return 0;
}
I don't disagree with 康桓瑋's answer, but I found it a little hard to follow, so let me explain it with a different example. Consider the following program:
#include <functional>
#include <iostream>
#include <typeinfo>
#include <type_traits>
struct tracer {
tracer() { std::cout << "default constructed\n"; }
tracer(const tracer &) { std::cout << "copy constructed\n"; }
tracer(tracer &&) { std::cout << "move constructed\n"; }
template<typename T> tracer(T &&t) {
if constexpr (std::is_same_v<T, const tracer>)
std::cout << "template constructed (const rvalue)\n";
else if constexpr (std::is_same_v<T, tracer&>)
std::cout << "template constructed (lvalue)\n";
else
std::cout << "template constructed (other ["
<< typeid(T).name() << "])\n";
}
};
int
main()
{
using fn_t = std::function<void()>;
const tracer t;
std::cout << "==== value capture ====\n";
fn_t([t]() {});
std::cout << "==== init capture ====\n";
fn_t([t = t]() {});
}
When run, this program outputs the following:
default constructed
==== value capture ====
copy constructed
template constructed (const rvalue)
==== init capture ====
copy constructed
move constructed
So what's going on here? First, note in both cases, the compiler must materialize a temporary lambda object to pass into the constructor for fn_t. Then, the constructor of fn_t must make a copy of the lambda object to hold on to it. (Since in general the std::function may outlive the lambda that was passed in to its constructor, it cannot retain the lambda by reference only.)
In the first case (value capture), the type of the captured t is exactly the type of t, namely const tracer. So you can think of the unnamed type of the lambda object as some kind of compiler-defined struct that contains a field of type const tracer. Let's give this structure a fake name of LAMBDA_T. So the argument to the constructor to fn_t is of type LAMBDA_T&&, and an expression that accesses the field inside is consequently of type const tracer&&, which matches the template constructor's forwarding reference better than the actual copy constructor. (In overload resolution rvalues prefer binding to rvalue references over binding to const lvalue references when both are available.)
In the second case (init capture), the type of the captured t = t is equivalent to the type of tnew in a declaration like auto tnew = t, namely tracer. So now the field in our internal LAMBDA_T structure is going to be of type tracer rather than const tracer, and when an argument of type LAMBDA_T&& to fn_t's constructor must be move-copied, the compiler will choose tracer's normal move constructor for moving that field.
For [v], the type of the lambda internal member variable v is const record, so when you
void call(const std::function<void()>&);
void print1(const record<string>& v) {
call([v] { });
}
Since [v] {} is a prvalue, when it initializes const std::function&, v will be copied with const record&&, and the template constructor will be chosen because it is not constrained.
In order to invoke v's copy constructor, you can do
void call(const std::function<void()>&);
void print1(const record<string>& v) {
auto l = [v] { };
call(l);
}
For [v=v], the type of the member variable v inside the lambda is record, so when the prvalue lambda initializes std::function, it will directly invoke the record's move constructor since record&& better matches.
I'm bound to C++03 and I have a non-copyable object (e.g. holding a resource).
I need to use move-and-swap semantics to be able to do something similar and avoid copies:
MyClass returnMyClass(void)
{
MyClass temp;
// fill 'temp' members with actual data
return temp;
}
int main(void)
{
MyClass test;
test = returnMyClass(); // need to avoid copies
}
Is it possible to respect all these requirements in C++03?
It is basically the same case of this, but for C++03.
To put the question in other words:
Given a non-copyable class MyClass, is it somehow possible in C++03 to do MyClass test = returnMyClass();?
I'm afraid the answer is simply no, but maybe I'm missing some trick.
There is no magic in move semantics. It's just another overload. The rvalue reference thing is a nice convenience that is not really essential.
template <class T>
struct rref {
rref (T& t) : t(t) {}
T& t;
};
template<class T>
rref<T> move(const T& t) {
return rref<T>(const_cast<T&>(t));
}
// you now can do a "move ctor"
class Foo {
Foo(rref<Foo>) { ... }
};
Now you also need NRVO to kick in for this to work. It is not guaranteed by the standard, but it is provided by pretty much every implementation. To ensure it really happens, you may declare but not define a copy ctor.
Full working demo
If you can initialize your object test at declaration you will be able to avoid copying because copy elision will be performed.
class MyClass
{
public:
MyClass(){std::cout << "Default Constructor" << std::endl;}
MyClass(const MyClass &){std::cout << "CPYConstructor" << std::endl;}
~MyClass() {std::cout << "Destructor" << std::endl;}
};
MyClass returnMyClass(void)
{
MyClass temp;
// fill 'temp' members with actual data
return temp;
}
int main(void)
{
MyClass test = returnMyClass(); // need to avoid copies
}
Output:
Default Constructor
Destructor
This question already has answers here:
On how to recognize Rvalue or Lvalue reference and if-it-has-a-name rule
(3 answers)
Closed 6 years ago.
class MyClass {
public:
MyClass()
{
std::cout << "default constructor\n";
}
MyClass(MyClass& a)
{
std::cout << "copy constructor\n";
}
MyClass(MyClass&& b)
{
std::cout << "move constructor\n";
}
};
void test(MyClass&& temp)
{
MyClass a(MyClass{}); // calls MOVE constructor as expected
MyClass b(temp); // calls COPY constructor... not expected...?
}
int main()
{
test(MyClass{});
return 0;
}
For the above code, I expected both object creations in test() to call move constructor because b is a r-value reference type (MyClass&&).
However, passing b to the MyClasss constructor does not call move constructor as expected, but calls copy constructor instead.
Why does second case calls copy constructor even if the passed argument is of type MyClass&& (r-value reference)???
I am using gcc 5.2.1. To reproduce my results, you must pass -fno-elide-constructors option to gcc to disable copy elision optimization.
void test(MyClass&& temp)
{
if (std::is_rvalue_reference<decltype(temp)>::value)
std::cout << "temp is rvalue reference!!\n";
else
std::cout << "temp is NOT rvalue!!\n";
}
Above code prints out "temp is rvalue reference" even if temp is named.
temp's type is rvalue reference type.
void test(MyClass&& temp)
{
MyClass a(MyClass{}); // calls MOVE constructor as expected
MyClass b(temp); // calls COPY constructor... not expected...?
}
That is because, temp (since it has a name,) is an lvalue reference to an rvalue. To treat as an rvalue at any point, call on std::move
void test(MyClass&& temp)
{
MyClass a(MyClass{}); // calls MOVE constructor as expected
MyClass b(std::move(temp)); // calls MOVE constructor... :-)
}
In the line commented by ***, why is Bar's copy constructor called? input_bar is a rvalue reference, so I expect the move constructor to be called. Did it convert to an lvalue reference? I can make the move constructor call if I change that line to bar_(std::move(input_bar)).
#include <iostream>
#include <array>
#include <memory>
class Bar
{
public:
Bar(const Bar& bar)
{
std::cout << "copy constructor called" << std::endl;
}
Bar(Bar&& bar)
{
std::cout << "move constructor called" << std::endl;
}
};
class Foo
{
public:
Foo(Bar&& input_bar) :
bar_(input_bar) // ***
{
}
Bar bar_;
};
int main()
{
Bar bar;
Foo foo(std::move(bar));
return 0;
}
Once an entity has a name, it is clearly an lvalue! If you have a name for an rvalue reference, the entity with the name is not an rvalue but an lvalue. The entire point is that you know that this entity references an rvalue and you can legitimately move its content.
If you want to just forward the rvalueness to the next function you call, you'd use std::move(), e.g.:
Foo(Bar&& bar): bar_(std::move(bar)) {}
Without the std::move() the rvalue is considered to be owned by the constructor. With the std::move() it releases the ownership and passes it on to the next function.
You have to move rhrs:
Foo(Bar&& input_bar) :
bar_(std::move(input_bar)) // ***
{
}
The reasoning is that once we actually use the RHR, it should be semantially treated as out of scope. Forcing you to use std::move allows the following code to not be undefined:
Foo(Bar&& input_bar) {
std::cout << input_bar.baz << std::endl;//not undefined
bar_ = Bar{std::move(input_bar)};//input_bar is now essentailly destroyted
//std::cout << input_bar.baz << std::endl;//Undefined behavior, don't do this
}
The general rule of thumb is that only things without names can actually be used as RHR's...RHR as arguments have names, and thus will be treated as LHR untill you call a function on them.