[英]What is the best practice when defining const& and && version of a set method?
[英]C++11 Best Practice: When to accept by value vs const&?
我对“规则”感到满意,“规则”在您打算存储对象时按值接受,而在仅需要访问它时按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)) {}
};
在这里,您要么将获得1个副本和4个动作,要么获得5个动作。 如果所有构造函数都接受const引用,则它只能是1个副本。 现在,我必须权衡昂贵的举动(一本还是五本)。
处理这些情况的最佳建议是什么?
处理这些情况的最佳建议是什么?
我最好的建议是做自己在做的事情:自己考虑。 不要相信您听到的一切。 测量。
下面,我采用了您的代码,并使用打印语句对其进行了检测。 我还添加了第三种情况:从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&&)
因此,按值传递比传递左值和xvalue个案的重载引用要花费额外的移动构造。 正如您已经指出的,移动结构不一定便宜。 它可能与复制结构一样昂贵。 从好的方面来说,您只需要编写1个带有值传递的重载即可。 通过重载的引用传递需要2 ^ N个重载,其中N是参数的数量。 在N == 1时相当可行,在N == 3时变得笨拙。
同样,正如您指出的,您的第二个例子只是您的第一个例子。
当性能是您最关心的问题时,尤其是当您不能依靠便宜的move构造函数时,请传递重载的右值引用。 当您可以依靠便宜的搬家结构,和/或不想处理不合理的(您自己要定义不合理的)过载时,请使用值传递。 在每种情况下,没有一个答案永远适合所有人。 C ++ 11程序员仍然必须思考。
您没有将其作为选择,但是我可以建议完美的转发吗?
class Doer
{
public:
template<typename T>
Doer(T&& thing) : m_Thing(std::forward<T>(thing)) {}
private:
Thing m_Thing;
};
这将导致左值/右值的单个复制/移动,这正是我们想要的。
仅提供部分答案,但在您提供的第二个示例中,可以通过显式继承A的基本ctor来避免移动:
struct B : A
{
using A::A;
};
struct C : B
{
using A::A;
};
struct D : C
{
using A::A;
};
在这里,您将获得1个副本对1个动作。
以下是一些可以解决该问题的其他规则:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.