Below is simplified example of the templated List
, where are two append_move()
and append_forward()
functions that have the same goal, take the arguments and emplace them in to the container List
. The first append_move()
function takes arg1
passed by value and then moves it to the emplace_back()
function. The second append_move()
function uses autodeduction of arg1
and then forwards it to the emplace_back()
function.
Does the append_forward()
function have any advantages over the append_move()
function and which function should be preferred?
#include <deque>
#include <string>
#include <utility>
template<typename T>
class List
{
public:
void append_move(T arg1, std::string arg2)
{
list.emplace_back(std::move(arg1), std::move(arg2));
}
template<typename X>
void append_forward(X&& arg1, std::string arg2)
{
list.emplace_back(std::forward<X>(arg1), std::move(arg2));
}
private:
std::deque<std::pair<T, std::string>> list;
};
If T
's destructor can't be optimized out and produces visible side-effects, e.g.
struct Loud { ~Loud() { std::cout << "destructor\n"; } };
then
List<Loud> list;
Loud const loud;
list.append_forward(loud);
calls one less destructor than
list.append_move(loud);
because the latter constructs one more object => has to call one more destructor. So, forwarding is more preferable.
However, it makes the API less pretty:
another_list.append_move({"some", "arguments"}); // works and pretty
//another_list.append_forward({"some", "arguments"}); // doesn't compile
another_list.append_forward(Foo{"some", "arguments"}); // works
So, unfortunately, providing hand-written overloads seems to be the best solution (for the user of your code) out of these three:
void append_overload(T&& t); // TODO actually implement
void append_overload(T const& t) { append_overload(T{t}); }
However (once again), if you care about all of this, consider emplacing:
template<typename... As> void append_emplace(As&&... args) {
list.emplace_back(std::forward<As>(args)...);
}
// works and pretty as long as you don't need to pass-construct two arguments
another_list.append_emplace("some", "arguments");
Besides, if you have more append_*
overloads, note that forwarding/emplacing versions are less prioritized because of being templates. The choice is yours.