首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++11最佳实践:什么时候接受价值与价值?

C++11最佳实践:什么时候接受价值与价值?
EN

Stack Overflow用户
提问于 2013-02-17 13:04:03
回答 5查看 1.6K关注 0票数 2

当您打算存储对象时,我对“按值接受”规则感到满意,而当您只需要访问对象时,则接受const引用。这样,您(类的编写者)就不会选择是否复制类变量的用户或是否移动它。但在使用中,我越来越不确定这一建议是否正确;搬迁在成本运作上并不统一.

代码语言:javascript
复制
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引用并复制对象一次,而是移动它两次(实际上是复制它)。用户在您的论点中执行一次,而您对您的成员执行一次。

即使移动比复制便宜得多,这种情况也会进一步恶化(假装移动的东西在下面是一些未知的成本,但比复制更便宜):

代码语言:javascript
复制
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移动。

处理这些情况最好的建议是什么?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2013-02-17 17:26:38

处理这些情况最好的建议是什么?

我最好的建议是做你正在做的事情:为自己想想。不要相信你听到的一切。测量。

下面我已经获取了您的代码,并使用print语句对其进行了测试。我还添加了第三种情况:从prvalue初始化:

测试尝试了两种方法:

  1. 通过价值传递。
  2. 过载通过const&&&

代码:

代码语言:javascript
复制
#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编译时,我得到:

代码语言:javascript
复制
lvalue
Thing(const Thing&)
Thing(Thing&&)

xvalue
Thing(Thing&&)
Thing(Thing&&)

prvalue
Thing(Thing&&)

对于-DPROCESS=2:

代码语言:javascript
复制
lvalue
Thing(const Thing&)

xvalue
Thing(Thing&&)

prvalue
Thing(Thing&&)

因此,对于lvalue和xvalue情况,传递值比传递重载的引用要花费额外的迁移构造。正如你已经注意到的,移动建筑并不一定便宜。它可以和复制结构一样昂贵。从好的方面来说,你只需要写一个过载的传递值.传递重载引用需要2^N重载,其中N是参数数。在N==1做的很好,在N==3变得很笨重。

另外,正如您所注意到的,第二个示例只是您的第一个例子,更糟了。

当性能是您最关心的问题时,特别是当您不能指望廉价的移动构造函数时,请传递重载的rvalue引用。当你可以指望一个便宜的移动结构,和/或你不想处理一个不合理的(你自己定义不合理)的超载,使用传递的值。在每一种情况下,没有一种答案对每个人都是正确的。C++11程序员仍然需要思考。

票数 4
EN

Stack Overflow用户

发布于 2013-02-17 13:29:43

经常被引用的想要速度吗?通过价值传递。给了这个问题一个详细的处理方法:

..。尽管编译器通常需要在函数参数通过值传递时(因此对函数内参数的修改不会影响调用方)创建副本,但当源是rvalue时,编译器允许编辑副本,只使用源对象本身。

票数 1
EN

Stack Overflow用户

发布于 2013-02-17 18:00:46

你没有包括它作为一个选择,但我可以建议完美的转发吗?

代码语言:javascript
复制
class Doer
{
public:
    template<typename T>
    Doer(T&& thing) : m_Thing(std::forward<T>(thing)) {}

private:
    Thing m_Thing;
};

这将导致lvalue/rvalue的一个副本/移动,这正是我们想要的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14921364

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档