C++中传递std::string的最快方法是什么?

3
注意:我提出这个问题是作为一个特定于std::string的问题,而不是一般的“如何传递对象”的问题。我想删除这个问题,但由于它已经有了答案,所以我不能删除它。我相信答案可能会趋向于回答更一般的问题:“在C++中传递对象的最佳方法是什么?”即使这个具体的问题本身可能不是一个重复的问题,在stackoverflow上有很多这些更一般的答案的副本。这种退化可能是因为这个问题是一个不恰当的问题,std::string类并没有什么特别之处。这个问题没有得到高票,这表明它不太有趣。如果是这样,那就是我的错。也许管理员会看到这段黑色文字,对我表示怜悯,并删除这个问题。
以下哪个签名代表着将非const的std::string实例传递给一个不修改它的函数的最快方法,考虑到在调用站点包括是否在调用之前生成底层字符数组的深度复制的总体开销?
extern void eat_string_by_value          (std::string s);
extern void eat_const_string_by_value    (const std::string s);
extern void eat_string_by_reference      (std::string& s);
extern void eat_const_string_by_reference(const std::string& s);

注意:我提出这个问题是一个与std::string特定相关的问题,而不是一个关于一般对象传递的问题。特别是,我的问题是由以下问题引发的:

  • std::string句柄体实现:其中任何一个签名是否意味着在调用站点深度复制字符串体及其后备字符数组?
    • 请注意复制句柄(应该是轻量级的)和复制主体(它将涉及内存分配)之间的区别。
  • C++98与C++>=11: 在这个版本划分的两侧是否有不同的答案?

我不认为我的问题是这个以前的问题的重复,这个问题本身被标记为一般对象传递问题的重复<Pass arguments as std::string or const std::string&?> ,因为这些类型特定的详细说明。

一个相关问题的有趣答案:


4
你测量过它了吗? - Baum mit Augen
7
除非函数内部要复制字符串,否则请使用常量引用。如果函数要在内部复制字符串,则答案是“这很复杂”。 - T.C.
2
为什么会将非const参数传递给不修改参数的函数? - jterrace
1
为什么需要非常量才能打印? - jterrace
1
这是一个类似的问题:https://dev59.com/qWkv5IYBdhLWcg3w9lai - BЈовић
显示剩余9条评论
2个回答

7
extern void eat_string_by_value          (std::string s);        // value
extern void eat_const_string_by_value    (const std::string s);
extern void eat_string_by_reference      (std::string& s);       // reference
extern void eat_const_string_by_reference(const std::string& s); // const-ref

首先,您错过了一个选项(自C++11以来):

extern void eat_string_by_rvaluereference(std::string&& s);      // rvalue-ref

下一个要点是,你的两个选项是一样的,因为顶层参数cv限定符不是函数签名的一部分:
extern void eat_string_by_value          (std::string s);
extern void eat_const_string_by_value    (const std::string s);

那么,让我们来看看:
  1. 按值传递:
    劣势:调用者必须将参数复制或移动。除非它在那里构建。
    优势:绑定到所有内容。被调用者有自己的副本可以修改。
  2. 按引用传递: 劣势:只绑定到lvalue(非临时对象)。
    优势:被调用者可以修改传递的参数。
  3. 按const-ref传递。 劣势:如果需要,被调用者必须制作自己的副本。
    优点:绑定到所有内容。
  4. 按rvalue-ref传递。
    劣势:不能绑定到lvalue。如果绑定到const对象,则复制。
    优点:可以处理传递的参数。
因此,按值传递 几乎总是意味着复制(或至少是移动),按引用传递 意味着永远不会复制(但对参数有不必要的限制),其余所有情况只有在必要时才进行复制。
按const-ref传递 表示参数无法修改,而 按rvalue-ref传递 表示其资源可以重新使用,这可能会节省以后的复制。

2
以下哪种签名表示将非const std::string实例快速传递到不修改它的函数中,考虑调用站点的总体开销,包括在调用之前是否会生成底层字符数组的深度副本?
这取决于具体情况。没有一种方式是在所有方面和所有情况下都最好的。
主要变量:
- 你正在传递什么?字符串的长度可以影响哪种形式的性能更好。 - 编译器 - 编译器选项和语言标准 - 标准库 - 调用站点是否需要一个副本? - 函数内的字符串会发生什么? - 按值传递可以打开某些优化的可能性,但复制的成本通常更高。
没有一种答案/签名是每种情况下都理想的。
std::string处理主体实现:签名中是否意味着调用站点将对字符串主体及其支持字符数组进行深度复制?
某些实现使用COW(写时复制),但在C++11中不再有效。传递值将需要复制。如果您的库实现了SSO,则短字符串的复制可能很快(=无堆分配)。
C++98与C++>=11:这个版本划分线两侧是否有不同的答案?
如上所述,COW不再有效。你可能会看到非常不同的性能特征。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接