首页 > 解决方案 > 在继承层次结构中复制和移动

问题描述

考虑一个简单的

class Foo
{
public:
   Foo(const std::string& name) : _name{name}

private:
   std::string _name;
};

我们知道可以通过复制和移动来优化

class Foo
{
public:
   Foo(std::string name) : _name{std::move(name)}

private:
   std::string _name;
};

但现在考虑层次结构:

class Base
{
protected:
   Base(const std::string& name) : _name{name}

private:
   std::string _name;
};

class Derived : public Base
{
public:
   Derived(const std::string& name) : A(name) {}
};

class Derived2 : public Derived
{
public:
   Derived2(const std::string& name) : Derived(name) {}
};

在这里实现复制和交换习语的正确方法是什么?它甚至有使用复制和交换而不是传递 const 引用的意义吗?

编辑:请解释否决票。

标签: c++move

解决方案


通过const 引用传递仍然是传统智慧

并且很可能会继续存在,请参阅有关此的代码指南。

尽管您可能在博客等上读到了什么,但您应该坚持为对象的所有正常用例传递const 引用,这些对象的复制成本可能很高 - 从语义上讲,您不必担心类型 X 如何执行移动和复制。这仍然是推荐的传统智慧,并为后期更改实施提供了所需的灵活性。如果您将代码内联留在标头中,则任何体面的编译器都可以对其进行优化(或者如果不在标头中,则可能进行整个程序优化)。

此外,对于右值而不是左值,使用复制和移动仅(有时)更快。lvalues 实际上现在更糟了,必须先获取对象的副本,然后再移动它。

std::string由于小字符串优化,这里特别有趣 - 所以对于许多小字符串来说,在最坏的情况下可能会导致性能下降接近 2 倍。为什么?对于小字符串,移动与复制一样昂贵,移动或复制都不是微不足道的,这意味着如果使用复制和移动习语,则会出现优化失误。

在来自评论的链接中,作者还必须写:

同样,这是一种廉价的恒定时间操作,而复制是一种线性时间操作,因此在许多情况下,这是值得付出的代价。

此外,并非所有类型的移动都很便宜(示例std::array)。

当且仅当,您已经在分析器中显示了它并且您仍然非常关心它 - 那么您可能想要使用前向引用 &&而不是普通副本(如果不是解决方案是第三个) - 然后调用std::forward用于转发到基类(完美转发)。


推荐阅读