Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/126.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ std::string实现中的这种优化是允许的吗?_C++_Optimization_Standard Library_Proxy Object - Fatal编程技术网

C++ std::string实现中的这种优化是允许的吗?

C++ std::string实现中的这种优化是允许的吗?,c++,optimization,standard-library,proxy-object,C++,Optimization,Standard Library,Proxy Object,我正在考虑std::string::substr的实现。它返回一个新的std::string对象,这对我来说似乎有点浪费。为什么不返回一个对象,该对象引用原始字符串的内容,并且可以隐式地分配给std::string?对实际复制的一种惰性评估。这样的类可能看起来像这样: template <class Ch, class Tr, class A> class string_ref { public: // not important yet, but *looks* like

我正在考虑std::string::substr的实现。它返回一个新的
std::string
对象,这对我来说似乎有点浪费。为什么不返回一个对象,该对象引用原始字符串的内容,并且可以隐式地分配给
std::string
?对实际复制的一种惰性评估。这样的类可能看起来像这样:

template <class Ch, class Tr, class A>
class string_ref {
public:
    // not important yet, but *looks* like basic_string's for the most part

private:
    const basic_string<Ch, Tr, A> &s_;
    const size_type pos_;
    const size_type len_;    
};
std::string s1 = "hello world";
std::string s2 = "world";
if(s1.substr(6) == s2) {
    std::cout << "match!" << std::endl;
}
总共构造不超过2个
std::string
对象。对于执行大量字符串操作的代码来说,这似乎是一个有用的优化。当然,这不仅适用于
std::string
,而且适用于任何可以返回其内容子集的类型

据我所知,没有实现能做到这一点

我想问题的核心是:

给定一个可以根据需要隐式转换为
std::string
的类,库编写器将成员的原型更改为返回类型是否符合标准?或者更一般地说,作为一种优化,库编写者是否有余地在这些类型的情况下返回“代理对象”而不是常规对象

我的直觉是这是不允许的,原型必须完全匹配。考虑到您不能仅在返回类型上重载,这就不会给库编写器留下任何空间来利用这些类型的情况。就像我说的,我想答案是否定的,但我想我会问:-)。

这个想法是正确的,但你不需要压缩整个缓冲区,而是要跟踪缓冲区的哪个子集是“真实”字符串。(COW以其正常形式在一些库实现中使用。)

因此,您根本不需要代理对象或更改接口,因为这些细节可以完全内部化。从概念上讲,您需要跟踪四件事:源缓冲区、缓冲区的引用计数以及该缓冲区中字符串的开始和结束

只要操作修改缓冲区,它就会创建自己的副本(从开始和结束分隔符开始),将旧缓冲区的引用计数减少1,并将新缓冲区的引用计数设置为1。其余的引用计数规则是相同的:复制并将计数增加1,销毁字符串并将计数减少1,达到零并删除,等等


substr
只创建一个新的字符串实例,除非明确指定了开始和结束分隔符。

由于
substr
返回
std::string
,因此无法返回代理对象,而且他们不能仅更改其返回类型或重载(出于您提到的原因)

他们可以通过使
字符串本身能够成为另一个字符串的子字符串来实现这一点。这意味着对所有使用都会有内存损失(保存一个额外的字符串和两个size_类型)。此外,每个操作都需要检查它是否具有字符或是代理。也许这可以通过一个实现指针来实现——问题是,现在我们正在使一个通用类在可能的边缘情况下变得更慢

如果需要,最好的方法是创建另一个类,
substring
,它从字符串、位置和长度构造,并转换为字符串。您不能将其用作s1.substr(6)
,但您可以这样做

 substring sub(s1, 6);

您还需要创建使用子字符串和字符串的公共操作,以避免转换(因为这就是关键所在)。

关于您的具体示例,这对我很有用:

if (&s1[6] == s2) {
    std::cout << "match!" << std::endl;
}
if(&s1[6]==s2){

std::cout这是一个非常著名的优化,使用相对广泛,称为写时复制或COW。基本的事情甚至不是处理子字符串,而是处理像

s1 = s2;

现在,这个优化的问题是,对于应该支持多个线程的目标,C++库应该使用原子操作访问字符串的引用计数(或者更糟,如果目标平台不提供原子操作,则用互斥体保护)。。这非常昂贵,因此在大多数情况下,简单的非COW字符串实现速度更快

见GOTW#43-45:


<> P>更糟的是,使用过GUW的库,比如GNU C++库,不能简单地恢复到简单的实现,因为这会破坏ABI。(尽管C++ +0x到了救援,无论如何,这将需要一个ABI BUMP!):

< P>你所说的是(或)Java的
Java.lang.String
class()的核心特性之一。在许多方面,Java的
String
类和C++的
basic_String
模板的设计是相似的,因此我可以想象,利用这种“子字符串优化”编写
basic_String
模板的实现是可能的

<>你要考虑的一件事是如何编写<代码> CyScript()的实现。const
成员。根据字符串作为另一个字符串的子字符串的位置,它可能必须创建一个新副本。如果请求c_str的字符串不是尾随子字符串,它肯定必须创建内部数组的新副本。我认为这需要在大多数情况下使用
mutable
关键字,如果n总之,
basic\u string
实现的数据成员,由于编译器不再能够帮助程序员实现常量的正确性,因此大大复杂了其他
const
方法的实现

编辑:实际上,为了适应
c_str()常量
data()常量
,您可以使用一个类型为
const charT*
的可变字段。初始设置为
NULL
,它可以是每个实例,只要
c_str(),就可以初始化为指向新的
图表
数组的指针常量
数据()常量
ar