应该首选什么,移动或转发参数

What should be preferred, moving or forwarding arguments

下面是模板化 List 的简化示例,其中有两个具有相同目标的 append_move()append_forward() 函数,获取参数并将它们放入容器 List。第一个 append_move() 函数接受 arg1 按值传递,然后将其移动到 emplace_back() 函数。第二个 append_move() 函数使用 arg1 的自动推导,然后将其转发给 emplace_back() 函数。

append_forward() 函数与 append_move() 函数相比有什么优势吗?应该首选哪个函数?

#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;
};

前进或移动

如果T的析构函数无法优化并产生可见的副作用,例如

struct Loud { ~Loud() { std::cout << "destructor\n"; } };

然后

List<Loud> list;
Loud const loud;
list.append_forward(loud);

调用的析构函数比

少一个
list.append_move(loud);

因为后者多构造了一个对象=> 又要调用一个析构函数。所以,转发更可取。

然而,它使 API 不那么漂亮:

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

手动超载

因此,不幸的是,提供手写重载似乎是这三个中最好的解决方案(对于您的代码的用户):

void append_overload(T&& t); // TODO actually implement
void append_overload(T const& t) { append_overload(T{t}); }

安置

但是(再一次),如果您关心所有这些,请考虑放置:

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");

其他重载

此外,如果您有更多 append_* 重载,请注意 forwarding/emplacing 版本的优先级较低,因为它们是模板。选择权在你。