下面是一个具有一个字符串成员的类。我们想在构造函数中对其进行初始化:
class MyStr {
std::string m_str;
public:
MyStr(const std::string& rstr) : m_str(rstr) {}
};
构造函数采用const std :: string&。
我们可以将常量引用替换为string_view:
MyStr(std::string_view strv) : m_str(strv) {}
或按值传递字符串并从中移出:
MyStr(std::string str) : m_str(std::move(str)) {}
首选哪一种?
3例:
MyStr mystro1{"Case 1: From a string literal"};
std::string str2 { "Case 2: From l-value"};
MyStr mystro2 { str2 };
std::string str3 { "Case 3: From r-value reference"};
MyStr mystro3 { std::move(str3) };
答案 0 :(得分:2)
最有效的方法是完善转发可转换为std::string
的任何参数:
class MyStr {
std::string m_str;
public:
template<class T,
class = std::enable_if_t<std::is_convertible<std::remove_reference_t<T>, std::string>::value>>
MyStr(T&& arg)
: m_str(std::forward<T>(arg))
{}
};
虽然有点大嘴。第二个最佳选择是根本不提供构造函数,不公开数据成员,并使用聚合初始化语法初始化MyStr
。
完整的故事:CppCon 2017: Nicolai Josuttis “The Nightmare of Move Semantics for Trivial Classes”。
答案 1 :(得分:1)
这是实践经验法则,适用范围更广:
按值传递所有廉价移动 †接收参数,并在适当时使用
std::move
。†主要示例是不可 廉价移动的类型:
std::array
和其他类型{{1} },其中T
是“大”
这通常是可用性(在接口的任一侧)和计算开销之间的良好折衷。通常,额外的工作(相对于完美的转发)最多是一个额外的移动构造,最多是一个额外的解构。除非您确定了应用程序的性能瓶颈,否则您很可能不想花费更多的编程精力。
This blog post on how to pass sink arguments包含了关于替代方案的更长的讨论,其结论也从“坚持按值传递”作为默认值开始。
应用于您的sizeof(T)
成员示例,这表明
std::string
除了上面提到的一般性能方面,请注意,签名class MyStr {
std::string m_str;
public:
MyStr(std::string rstr) : m_str(std::move(rstr)) {}
};
意味着MyStr::MyStr(std::string rstr)
存储着以{{1}形式传递的事物的真实副本。 }。这可以帮助用户了解如何使用MyStr
。
答案 2 :(得分:0)
正如clonk所说,您需要确切指定您想要什么。您在寻找性能还是其他东西?
另外,您可以使用编译器浏览器对此进行比较
在编译器浏览器中编写代码,它将自动生成汇编代码。当然,您可以选择编译器。
答案 3 :(得分:0)
完整答案:Analysing the Cases