I thought to myself that I don't need std::forward<T>(arg);
in my function because I wasn't passing the argument on to another function, I was using it directly. However then I thought even if I use it directly, by for example assigning it, or using it as a constructor argument then those each are function calls, respectively to operator=
and constructor, which is a function call:
template <typename element_T>
void push_back(element_t&& copy)
{
*_end = copy; // This calls operator=, which is a function
}
template <typename ... ConstructorArgs>
void emplace_back(ConstructorArgs&& ... args)
{
new (_end) element_t(args);
// Calls constructor, needs (std::forward<ConstructorArgs>(args)...) ?
}
Do I need the calls to std::forward
in these cases?
An expression that is a name of a variable is always an lvalue. For example, in
1 template <typename T>
2 void push_back(T&& copy) {
3 *_end = copy;
4 }
copy
in line 3 has the lvalue value category no matter what type is deduced for T
. Depending on how operator=
in line 3 is defined/overloaded, this might result in selecting a wrong overload or in wrong type deduction.
Consider the following simple example (that follows the Ted Lyngmo's example from the comments section):
struct A {
void operator=(const A&); // (1)
void operator=(A&&); // (2)
};
struct C {
template<class T>
void push_back(T&& copy) {
a = copy;
}
A a;
};
C{}.push_back(A{});
Which A
's assignment operator will be invoked here? One might expect (2) because A{}
is a prvalue, but the correct answer is (1), because copy
is an lvalue, and lvalues can't bind to rvalue references.
If we change the assignment to
a = std::forward<T>(copy);
then the expression std::forward<T>(copy)
will have the xvalue value category and the (2) assignment operator will be called, as expected.
Placement new follows the same reasoning.