假设我有一个类,我计划将其作为可实例化的类直接公开给程序员:
class Base
{
public:
Base(std::string text) : m_text(std::move(text)) {}
private:
std::string m_text;
};到目前一切尚好。这里不需要rvalue构造函数。现在,在将来的某个时候,我决定扩展Base:
class Derived : public Base
{
public:
Derived(const std::string &text) : Base(text) {}
};这让我感到困惑:我不能在Derived中通过值来获取字符串,因为这就是Base已经在做的事情--我最终会得到2个副本和1个移动。这里的const-reference构造函数还对rvalue执行不必要的复制。
问题:如何在不添加更多构造函数的情况下只复制+移动一次(就像Base中的简单构造函数一样)?
发布于 2013-05-24 03:52:44
您不能只复制和移动一次,除非您更改类的设计,并将它们的构造函数转换为(可能受SFINAE约束的)模板化转发构造函数(Yakk's answer shows how)。
虽然这样做可以在提供右值时只执行一次移动而不执行复制,而在提供左值时只执行一次复制而不执行移动,但在大多数情况下这是一种过度的杀伤力。
作为基于模板的转发构造函数的替代方法,您可以在基类和派生类中提供两个构造函数:一个用于右值引用,另一个用于对const的左值引用。但是,在大多数情况下,这是一个不必要的复杂性(当参数数量增加时,也不能很好地扩展,因为所需构造函数的数量将呈指数级增加)。
移动std::string的速度与复制指针和整数一样快(这里忽略了单点登录优化),除非您有确凿的证据证明这是阻碍应用程序满足其性能要求的瓶颈(难以相信),否则您不应该为此而烦恼。
因此,只要让您的Derived构造函数无条件地按值接受其参数,并在将其传递给基类的构造函数时移动它:
class Derived : public Base
{
public:
Derived(std::string text) : Base(std::move(text)) { }
};如果您希望(或接受) Derive继承Base的所有构造函数,另一种选择是利用C++11继承的构造函数,如下所示:
class Derived : public Base
{
public:
using Base::Base;
// ^^^^^^^^^^^^^^^^^
};发布于 2013-05-24 03:53:48
使用SFINAE构造函数实现完美的转发:
class Derived : public Base
{
public:
template<
typename T,
typename=typename std::enable_if<
std::is_constructible<std::string, T&&>::value
>::type
>
Derived(T&& text) : Base(std::forward<T>(text)) {}
};一个缺点(除了上述构造的疯狂之外)是,这将以过于贪婪的程度参与重载解析:对于任何可以转换为std::string的参数,它几乎都是完美的匹配。因此,如果您有另一个接受char const*的构造函数,那么如果您传入了一个char const*变量,那么它可能会被忽略,因为T&&可能匹配得更好。
如果你的Derived或Base有一个operator std::string...
发布于 2013-05-24 04:00:01
将Derived更改为以下内容:
class Derived : public Base
{
public:
using Base::Base;
};现在,Derived从Base继承了构造函数。
有关详细信息,请参阅Forwarding all constructors in C++0x。
https://stackoverflow.com/questions/16722614
复制相似问题