C++ 11:按值调用,移动语义和继承

sho*_*rke 4 c++ inheritance move move-semantics c++11

假设我有一个类,我打算将它作为可实例化的类直接暴露给程序员:

class Base
{
public:
    Base(std::string text) : m_text(std::move(text)) {}
private:
    std::string m_text;
};
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.这里不需要rvalue构造函数.现在,在未来的某个时刻,我决定扩展Base:

class Derived : public Base
{
public:
    Derived(const std::string &text) : Base(text) {}
};
Run Code Online (Sandbox Code Playgroud)

这让我感到困惑:我无法在Derived中按值获取字符串,因为这就是Base已经在做的事情 - 我最终会得到2个副本和1个移动.这里的const-reference构造函数还对rvalues执行不必要的复制.

问题是:如何复制+仅移动一次(就像Base中的简单构造函数一样)而不添加更多构造函数?

And*_*owl 8

您不能只复制和移动一次,除非您更改类的设计并将其构造函数转换为(可能是SFINAE约束的)模板化转发构造函数(Yakk的答案显示如何).

虽然这样做可以在提供rvalues时只执行一次移动而不执行复制,并且在提供左值时只执行一次复制而不移动,但在大多数情况下这是一种过度杀伤.

作为基于模板的转发构造函数的替代,您可以在基类和派生类中提供两个构造函数:一个用于rvalue引用,另一个用于lvalue引用const.但同样,这在大多数情况下都是不必要的并发症(并且当参数数量增加时不能很好地扩展,因为所需构造函数的数量将呈指数增长).

移动std::stringa与复制指针和整数一样快(在这里忽略SSO优化),除非你有真实证据证明这是阻止你的应用程序满足其性能要求的瓶颈(很难相信),否则你不应该为此烦恼. .

因此,只需让你的Derived构造函数无条件地通过值获取它的参数,并在将它传递给基类的构造函数时移动它:

class Derived : public Base
{
public:
    Derived(std::string text) : Base(std::move(text)) { }
};
Run Code Online (Sandbox Code Playgroud)

另一种选择,如果你想(或接受)Derive继承所有Base的构造,是利用C++ 11的遗传构造,像这样:

class Derived : public Base
{
public:
    using Base::Base;
//  ^^^^^^^^^^^^^^^^^
};
Run Code Online (Sandbox Code Playgroud)

  • @ user2414893:如果你在谈论我的例子,那只是一个动作(当从临时`字符串`初始化`Derived :: Derived`的参数时)加上一个动作(当将该参数传递给`Base :: Base`时) )加一个移动(当移动到数据成员时).所以3招,没有副本. (2认同)