当您打算存储对象时,我对“按值接受”规则感到满意,而当您只需要访问对象时,则接受const引用。这样,您(类的编写者)就不会选择是否复制类变量的用户或是否移动它。但在使用中,我越来越不确定这一建议是否正确;搬迁在成本运作上并不统一.
struct Thing
{
std::array<int, 10000> m_BigArray;
};
class Doer
{
public:
Doer(Thing thing) : m_Thing(std::move(thing)) {}
private:
Thing m_Thing;
};
int main()
{
Thing thing;
Doer doer1(std::move(thing)); // user can decide to move 'thing'
// or
Doer doer2(thing); // user can decide to copy 'thing'
}在上面的例子中,移动和复制一样昂贵。因此,您不再使用const引用并复制对象一次,而是移动它两次(实际上是复制它)。用户在您的论点中执行一次,而您对您的成员执行一次。
即使移动比复制便宜得多,这种情况也会进一步恶化(假装移动的东西在下面是一些未知的成本,但比复制更便宜):
struct A
{
A(Thing thing) : m_Thing(std::move(thing)) {}
Thing m_Thing;
};
struct B : A
{
B(Thing thing) : A(std::move(thing)) {}
};
struct C : B
{
C(Thing thing) : B(std::move(thing)) {}
};
struct D : C
{
D(Thing thing) : C(std::move(thing)) {}
};在这里,您将有一个副本和4个移动,或5个移动结束。如果所有的构造函数都接受了一个const引用,那么它只能是一个副本。现在我必须权衡什么移动昂贵,1或5移动。
处理这些情况最好的建议是什么?
发布于 2013-02-17 17:26:38
处理这些情况最好的建议是什么?
我最好的建议是做你正在做的事情:为自己想想。不要相信你听到的一切。测量。
下面我已经获取了您的代码,并使用print语句对其进行了测试。我还添加了第三种情况:从prvalue初始化:
测试尝试了两种方法:
const&和&&代码:
#include <utility>
#include <iostream>
struct Thing
{
Thing() = default;
Thing(const Thing&) {std::cout << "Thing(const Thing&)\n";}
Thing& operator=(const Thing&) {std::cout << "operator=(const Thing&)\n";
return *this;}
Thing(Thing&&) {std::cout << "Thing(Thing&&)\n";}
Thing& operator=(Thing&&) {std::cout << "operator=(Thing&&)\n";
return *this;}
};
class Doer
{
public:
#if PROCESS == 1
Doer(Thing thing) : m_Thing(std::move(thing)) {}
#elif PROCESS == 2
Doer(const Thing& thing) : m_Thing(thing) {}
Doer(Thing&& thing) : m_Thing(std::move(thing)) {}
#endif
private:
Thing m_Thing;
};
Thing
make_thing()
{
return Thing();
}
int main()
{
Thing thing;
std::cout << "lvalue\n";
Doer doer1(thing); // user can decide to copy 'thing'
std::cout << "\nxvalue\n";
Doer doer2(std::move(thing)); // user can decide to move 'thing'
std::cout << "\nprvalue\n";
Doer doer3(make_thing()); // user can decide to use factor function
}对于我来说,当我用-DPROCESS=1编译时,我得到:
lvalue
Thing(const Thing&)
Thing(Thing&&)
xvalue
Thing(Thing&&)
Thing(Thing&&)
prvalue
Thing(Thing&&)对于-DPROCESS=2:
lvalue
Thing(const Thing&)
xvalue
Thing(Thing&&)
prvalue
Thing(Thing&&)因此,对于lvalue和xvalue情况,传递值比传递重载的引用要花费额外的迁移构造。正如你已经注意到的,移动建筑并不一定便宜。它可以和复制结构一样昂贵。从好的方面来说,你只需要写一个过载的传递值.传递重载引用需要2^N重载,其中N是参数数。在N==1做的很好,在N==3变得很笨重。
另外,正如您所注意到的,第二个示例只是您的第一个例子,更糟了。
当性能是您最关心的问题时,特别是当您不能指望廉价的移动构造函数时,请传递重载的rvalue引用。当你可以指望一个便宜的移动结构,和/或你不想处理一个不合理的(你自己定义不合理)的超载,使用传递的值。在每一种情况下,没有一种答案对每个人都是正确的。C++11程序员仍然需要思考。
发布于 2013-02-17 13:29:43
经常被引用的想要速度吗?通过价值传递。给了这个问题一个详细的处理方法:
..。尽管编译器通常需要在函数参数通过值传递时(因此对函数内参数的修改不会影响调用方)创建副本,但当源是rvalue时,编译器允许编辑副本,只使用源对象本身。
发布于 2013-02-17 18:00:46
你没有包括它作为一个选择,但我可以建议完美的转发吗?
class Doer
{
public:
template<typename T>
Doer(T&& thing) : m_Thing(std::forward<T>(thing)) {}
private:
Thing m_Thing;
};这将导致lvalue/rvalue的一个副本/移动,这正是我们想要的。
https://stackoverflow.com/questions/14921364
复制相似问题