应该首选什么,移动或转发参数
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 版本的优先级较低,因为它们是模板。选择权在你。
下面是模板化 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 版本的优先级较低,因为它们是模板。选择权在你。