最近,我在阅读有关转发参考文献的内容,偶然发现了这个常见的示例:
void put(const Data& data) { m_data = data; }
void put(Data&& data) {m_data = std::move(data);}
解决这个问题的方法是引入转发引用,如下所示:
template <class T>
void put(T&& data) {m_data = std::forward<T>(data);}
但是,我注意到还有另一种解决方案,它的工作原理似乎相同,但稍微干净一些:
void put(Data data) {m_data = std::move(data);}
这样,如果
put
要接收一些左值,则只会调用一个副本,如果它要接收右值,则只会调用移动操作。这与通用参考相同。
这也使用了更少的模板,并导致更容易出现编译错误。
鉴于此解决方案,转发引用的理由是什么 - 它可以提供简单的按值传递和移动习惯用法无法提供的功能?
谢谢!
转发的大部分目的是尽可能长时间地维护对原始对象的引用,并且仅在真正必要时在最后一刻才转换到目标。
您建议的替代方案是尽早转换为目标类型,然后根据情况移动目标类型以防止其导致问题。有时这可能会奏效。但其他人则不会。一种明显的情况是,您可以直接从源类型分配到目标类型,但根本无法移动目标类型:
#include <utility>
class Foo {
char const *m_data;
public:
Foo &operator=(char const *data) { m_data = data; return *this; }
Foo() {}
Foo(Foo const &) = delete;
Foo(Foo &&) = delete;
};
class Bar {
Foo m_data;
public:
template <class T>
void put(T &&origin) { m_data = std::forward<T>(origin); }
// void put2(Foo data) { m_data = data; }
};
int main() {
Bar b;
b.put("Some string");
}
在这里,如果我们取消注释
Bar::put2
(使用您的技术),代码将不再编译,因为它依赖于 Foo
允许移动(或复制),但事实并非如此。