在C++中,相当于CPython字符串连接

What is the equivalent of CPython string concatenation, in C++?

本文关键字:CPython 字符串 连接 相当于 C++      更新时间:2023-10-16

可能的重复:
简单字符串连接

昨天,当我写这篇文章时,有人问SO

如果我有一个字符串x='wow'在 Python 中应用函数add

x='wow'
x.add(x)
'wowwow'

如何在C++中做到这一点?

add(不存在)更正为__add__(标准) 方法)这是一个深刻而有趣的问题,涉及微妙的低 级别详细信息、高级算法复杂性注意事项,以及 甚至穿线!,但它的配方非常简短简洁。

我正在重新发布 原始问题 作为我自己的,因为我没有机会提供正确的 在删除之前回答,我努力恢复原始问题,所以 我可以帮助增加对这些问题的一般理解,失败了。

我已将原始标题"选择python或C++"更改为...

  • 在C++中,CPython 字符串串联的等价物是什么?

从而将问题缩小了一点。

代码段的一般含义。

给定的代码片段

x = 'wow'
x.__add__( x )

在 Python 2.x 和 Python 3.x 中有不同的含义。

在 Python 2.x 中,字符串默认是窄字符串,每个编码单元一个字节, 对应于基于C++char的字符串。

在Python 3.x中,字符串是宽字符串,保证代表Unicode, 对应于C++的实际用法 基于wchar_t的字符串,同样具有未指定的 2 或 4 个字节 每个编码单位。

不考虑效率,__add__方法在两个主要方法中表现相同 Python 版本,对应于std::basic_string的 C+++运算符 (即,对于std::stringstd::wstring),例如引用CPython 3k 文档:


object.__add__(self, other)...求值表达式x + y,其中x是具有__add__()方法的类的实例,调用x.__add__(y)

举个例子,CPython 2.7代码

x = 'wow'
y = x.__add__( x )
print y

通常写成

x = 'wow'
y = x + x
print y

并对应于以下C++代码:

#include <iostream>
#include <string>
using namespace std;
int main()
{
auto const x = string( "wow" );
auto const y = x + x;
cout << y << endl;
}

与给出的许多错误答案的主要区别 最初的问题, 是C++对应是一种表达,而不是更新

也许很自然地认为方法名称__add__表示更改 字符串对象的值,更新,但关于可观察 行为 Python 字符串是不可变的字符串。他们的价值观永远不会改变,因为 在Python代码中可以直接观察到。这与 Java 和 C#,但与C++的可变std::basic_string字符串非常不同。

CPython 中的二次到线性时间优化。

添加了CPython 2.4 以下 优化,仅适用于窄字符串

s = s + "abc"s += "abc"格式语句中的字符串串联 现在在某些情况下更有效地执行。此优化 不会出现在其他 Python 实现中,例如 Jython,所以你不应该 依靠它;在以下情况下,仍建议使用字符串的join()方法 想要有效地将大量字符串粘合在一起。(由Armin Rigo提供。

听起来可能不多,但在适用的情况下,这种优化 从二次时间O(n2) 减少串联序列 线性时间O(n),在最终结果的长度n中。

首先,优化用更新替换了连接,例如,好像

x = x + a
x = x + b
x = x + c

或者就此而言

x = x + a + b + c

被替换为

x += a
x += b
x += c

在一般情况下,将有许多对字符串对象的引用x指,并且由于 Python 字符串对象必须看起来是不可变的,因此第一个 更新分配无法更改该字符串对象。因此,它通常具有 创建一个全新的字符串对象,并将其(引用)分配给x

此时,x持有对该对象的唯一引用。这意味着 对象可以通过附加b的更新赋值进行更新,因为有 没有观察员。同样,对于c的附加.

这有点像量子力学:你无法观察到这个肮脏的东西 继续下去,当有人有可能观察时,它永远不会完成 阴谋诡计,但你可以推断它一定是在进行统计 你收集性能,因为线性时间与二次时间完全不同!

线性时间是如何实现的?好吧,随着更新相同的缓冲区策略 可以像C++std::basic_string一样加倍,这意味着 只需在每次重新分配缓冲区时复制现有缓冲区内容, 而不是每个追加操作。这意味着 复制的总成本在最终字符串大小中最坏的情况下是线性的,在 与总和相同(表示每个缓冲区加倍时的复制成本) 1 + 2 + 4 + 8 + ... + N 小于 2*N。

C++中的线性时间字符串串联表达式。

为了忠实地在C++中重现CPython代码片段,

  • 应捕获操作的最终结果和表达性质,

  • 并且应该捕获其性能特征!

将CPython__add__直接转换为C++std::basic_string+失败 可靠地捕获CPython线性时间。C+++字符串串联编译器可以像CPython一样进行优化 优化。或者不是 - 这意味着一个人已经告诉初学者 C++相当于 Python 线性时间运算,是二次运算的东西 时间 - 嘿,这是你应该使用的...

对于性能特征C+++=是基本答案,但是,这确实 没有抓住 Python 代码的表达式性质。

自然答案是线性时间C++字符串生成器类,它翻译 一个串联表达式到一系列+=更新,以便 Python 代码

from __future__ import print_function
def foo( s ):
print( s )
a = 'alpha'
b = 'beta'
c = 'charlie'
foo( a + b + c )    # Expr-like linear time string building.

大致对应

#include <string>
#include <sstream>
namespace my {
using std::string;
using std::ostringstream;
template< class Type >
string stringFrom( Type const& v )
{
ostringstream stream;
stream << v;
return stream.str();
}
class StringBuilder
{
private:
string      s_;
template< class Type >
static string fastStringFrom( Type const& v )
{
return stringFrom( v );
}
static string const& fastStringFrom( string const& s )
{ return s; }
static char const* fastStringFrom( char const* const s )
{ return s; }
public:
template< class Type >
StringBuilder& operator<<( Type const& v )
{
s_ += fastStringFrom( v );
return *this;
}
string const& str() const { return s_; }
char const* cStr() const { return s_.c_str(); }
operator string const& () const { return str(); }
operator char const* () const { return cStr(); }
};
}  // namespace my
#include <iostream>
using namespace std;
typedef my::StringBuilder S;
void foo( string const& s )
{
cout << s << endl;
}
int main()
{
string const    a   = "alpha";
string const    b   = "beta";
string const    c   = "charlie";
foo( S() << a << b << c );      // Expr-like linear time string building.
}