使用字符串成员构造对象的有效方法
Efficient way to construct object with string member
假设我有一个 class ThisHasAStringMember
。假设它有一个私有成员,它是一个字符串,我想有效地获取字符串值,在可能的情况下更喜欢移动而不是副本。以下两个构造函数可以实现吗?
class ThisHasAStringMember
{
public:
// ctors
ThisHasAStringMember(const std::string str) : m_str(str) {}
ThisHasAStringMember(std::string &&str) : m_str(std::move(str)) {}
// getter (no setter)
std::string value() { return m_str; }
private:
std::string m_str;
}
第二个构造函数中 str
参数前是否需要双符号?
这是完成此任务的正确方法吗?
起初我会注意到最好将构造函数标记为显式。
接下来最好更改解决方案中的第一个构造函数以采用 const 引用以避免复制左值:
// ctors
ThisHasAStringMember(const std::string& str) : m_str(str) {}
ThisHasAStringMember(std::string &&str) : m_str(std::move(str)) {}
从性能的角度来看,这种方法是最佳的(您将有一个复制构造函数调用左值和一个移动构造函数调用右值),但是在这种情况下每次实现两个构造函数是很无聊的。如果您有 N 个成员 - 2^N 个构造函数。
几乎没有其他选择:
Signle 构造函数,您可以在其中仅按值传递参数。是的,它在 C++98 中效率低下,但在 C++11 中创建完整副本时 - 这是一个选项。
ThisHasAStringMember(std::string str) : m_str(std::move(str)) {}
传递左值时,将调用一次复制构造函数和调用一次移动构造函数。当传递右值时,将有两个移动构造函数调用。是的,在每种情况下,您都有一个额外的移动构造函数调用。但它通常非常便宜(甚至可以被编译器优化掉)并且代码非常简单。
通过右值传递参数的单个构造函数:
ThisHasAStringMember(std::string&& str) : m_str(std::move(str)) {}
如果你传递左值,你必须首先在调用的地方明确地复制它,例如
ThisHasAStringMember(复制(someStringVar))。 (这里copy
是一种简单的模板复制方式)。而且你仍然会有一个额外的移动构造函数调用左值。对于右值,不会有开销。我个人喜欢这种方法:复制参数的所有地方都是明确的,你不会在 performance-critical 地方偶尔复制。
- 制作构造函数模板并使用完美转发:
template <typename String,
std::enable_if_t<std::is_constructible_v<std::string, String>>* = nullptr>
ThisHasAStringMember(String&& str) : m_str(std::forward<String>(str))
{}
右值和左值都没有开销,但在大多数情况下,您需要制作构造函数模板并在 header 中定义它。
假设我有一个 class ThisHasAStringMember
。假设它有一个私有成员,它是一个字符串,我想有效地获取字符串值,在可能的情况下更喜欢移动而不是副本。以下两个构造函数可以实现吗?
class ThisHasAStringMember
{
public:
// ctors
ThisHasAStringMember(const std::string str) : m_str(str) {}
ThisHasAStringMember(std::string &&str) : m_str(std::move(str)) {}
// getter (no setter)
std::string value() { return m_str; }
private:
std::string m_str;
}
第二个构造函数中 str
参数前是否需要双符号?
这是完成此任务的正确方法吗?
起初我会注意到最好将构造函数标记为显式。
接下来最好更改解决方案中的第一个构造函数以采用 const 引用以避免复制左值:
// ctors
ThisHasAStringMember(const std::string& str) : m_str(str) {}
ThisHasAStringMember(std::string &&str) : m_str(std::move(str)) {}
从性能的角度来看,这种方法是最佳的(您将有一个复制构造函数调用左值和一个移动构造函数调用右值),但是在这种情况下每次实现两个构造函数是很无聊的。如果您有 N 个成员 - 2^N 个构造函数。
几乎没有其他选择:
Signle 构造函数,您可以在其中仅按值传递参数。是的,它在 C++98 中效率低下,但在 C++11 中创建完整副本时 - 这是一个选项。
ThisHasAStringMember(std::string str) : m_str(std::move(str)) {}
传递左值时,将调用一次复制构造函数和调用一次移动构造函数。当传递右值时,将有两个移动构造函数调用。是的,在每种情况下,您都有一个额外的移动构造函数调用。但它通常非常便宜(甚至可以被编译器优化掉)并且代码非常简单。
通过右值传递参数的单个构造函数:
ThisHasAStringMember(std::string&& str) : m_str(std::move(str)) {}
如果你传递左值,你必须首先在调用的地方明确地复制它,例如
ThisHasAStringMember(复制(someStringVar))。 (这里copy
是一种简单的模板复制方式)。而且你仍然会有一个额外的移动构造函数调用左值。对于右值,不会有开销。我个人喜欢这种方法:复制参数的所有地方都是明确的,你不会在 performance-critical 地方偶尔复制。
- 制作构造函数模板并使用完美转发:
template <typename String,
std::enable_if_t<std::is_constructible_v<std::string, String>>* = nullptr>
ThisHasAStringMember(String&& str) : m_str(std::forward<String>(str))
{}
右值和左值都没有开销,但在大多数情况下,您需要制作构造函数模板并在 header 中定义它。