c++ - 在C++ 11中实现COW std::string的可能性

标签 c++ string c++11 language-lawyer standard-library

今天我通过了这个SO问题:Legality of COW std::string implementation in C++11

该问题的投票最多(35票赞成):

It's not allowed, because as per the standard 21.4.1 p6, invalidation of iterators/references is only allowed for

— as an argument to any standard library function taking a reference to non-const basic_string as an argument.

— Calling non-const member functions, except operator[], at, front, back, begin, rbegin, end, and rend.

For a COW string, calling non-const operator[] would require making a copy (and invalidating references), which is disallowed by the paragraph above. Hence, it's no longer legal to have a COW string in C++11.



我想知道这种调整是否有效,因为似乎C++ 03对字符串迭代器无效有类似的要求:

References, pointers, and iterators referring to the elements of a basic_string sequence may be invalidated by the following uses of that basic_string object:

  • As an argument to non-member functions swap() (21.3.7.8), operator>>() (21.3.7.9), and getline() (21.3.7.9).
  • As an argument to basic_string::swap().
  • Calling data() and c_str() member functions.
  • Calling non-const member functions, except operator[](), at(), begin(), rbegin(), end(), and rend().
  • Subsequent to any of the above uses except the forms of insert() and erase() which return iterators, the first call to non-const member functions operator[](), at(), begin(), rbegin(), end(), or rend().


这些与C++ 11的代码不完全相同,但是对于operator[]()的部分至少相同,原始答案被当作主要理由。所以我想,为了证明C++ 11中COW std::string实现的非法性,还需要引用其他一些标准要求。这里需要帮助。

这个问题已经闲置了一年多,所以我决定将其作为一个单独的问题提出。请让我知道这是否不合适,我将找到其他方法来消除我的疑问。

最佳答案

关键点是C++ 03标准中的最后一点。的
措词可能更清晰,但目的是第一个
在之后调用[]at等(但仅限第一个调用)
建立了新迭代器的东西(因此无效)
旧版本)可以使迭代器无效,但只能是第一个。的
实际上,C++ 03中的措词是一个快速的技巧,
回应法国国家机构对CD2的评论
C++ 98。最初的问题很简单:考虑:

std::string a( "some text" );
std::string b( a );
char& rc = a[2];

此时,通过rc进行的修改必须影响a,但是
不是b。但是,如果使用的是COW,则在调用a[2]时,ab共享一个表示;为了通过写
返回的引用不影响ba[2]必须为
被视为“写入”,并被允许使
引用。 CD2所说的是:对非常量的任何调用[]atbeginend函数之一
使迭代器和引用无效。法国国家机构
评论指出此a[i] == a[j]无效,
因为[]之一返回的引用是
被对方无效。您引用C++ 03的最后一点是
添加了规避此功能的方法-仅第一个调用[]
等可能会使迭代器无效。

我认为没有人会对结果感到完全满意。的
措词很快完成,尽管意图很明显
那些知道历史和原始问题的人,
我认为从标准来看还不是很清楚。此外,
一些专家开始质疑COW的值(value),
考虑到字符串类本身相对不可能
可靠地检测所有写入。 (如果a[i] == a[j]是完整的
表达式,没有写。但是字符串类本身必须
假设a[i]的返回值可能会导致写入。)
在多线程环境中,
写时复制所需的引用计数被认为是相对的
通常不需要的东西的成本很高。结果是
大多数实现(早就支持线程
C++ 11)无论如何都已经远离了COW;据我所知,
唯一仍在使用COW的主要实现是g++(但在那里
是其多线程实现中的已知错误)和
(也许)Sun CC(我上次看的是
由于管理计数器的成本而变得异常缓慢)。
我认为委员会只是把他们认为的
禁止COW进行清理的最简单方法。

编辑:

关于为何实现COW的更多说明
必须在第一次调用[]时使迭代器无效。考虑
天真的执行COW。 (我将其称为String,
忽略所有涉及特征和分配器的问题
在这里并不重要。我还将忽略异常,
线程安全,只是为了使事情尽可能简单。)
class String
{
    struct StringRep
    {
        int useCount;
        size_t size;
        char* data;
        StringRep( char const* text, size_t size )
            : useCount( 1 )
            , size( size )
            , data( ::operator new( size + 1 ) )
        {
            std::memcpy( data, text, size ):
            data[size] = '\0';
        }
        ~StringRep()
        {
            ::operator delete( data );
        }
    };

    StringRep* myRep;
public:
    String( char const* initial_text )
        : myRep( new StringRep( initial_text, strlen( initial_text ) ) )
    {
    }
    String( String const& other )
        : myRep( other.myRep )
    {
        ++ myRep->useCount;
    }
    ~String()
    {
        -- myRep->useCount;
        if ( myRep->useCount == 0 ) {
            delete myRep;
        }
    }
    char& operator[]( size_t index )
    {
        return myRep->data[index];
    }
};

现在想象一下,如果我写:
String a( "some text" );
String b( a );
a[4] = '-';

此后b的值是什么? (通过
(如果不确定的话)。)

显然,这是行不通的。解决方法是添加一个标志,bool uncopyable;StringRep,已初始化为false,并修改以下功能:
String::String( String const& other )
{
    if ( other.myRep->uncopyable ) {
        myRep = new StringRep( other.myRep->data, other.myRep->size );
    } else {
        myRep = other.myRep;
        ++ myRep->useCount;
    }
}

char& String::operator[]( size_t index )
{
    if ( myRep->useCount > 1 ) {
        -- myRep->useCount;
        myRep = new StringRep( myRep->data, myRep->size );
    }
    myRep->uncopyable = true;
    return myRep->data[index];
}

当然,这意味着[]将使迭代器无效,并且
引用,但仅限于第一次在对象上调用它。
下次,useCount将为1(图像将为
无法复制)。因此,a[i] == a[j]有效;不管哪个
编译器实际上首先评估(a[i]a[j]),然后评估第二个
人们会发现useCount为1,并且不必重复。
而且由于uncopyable标志,
String a( "some text" );
char& c = a[4];
String b( a );
c = '-';

也将起作用,并且不会修改b

当然,以上内容大大简化了。到达
在多线程环境中工作非常困难,
除非您简单地为任何功能获取整个功能的互斥量
可能会修改任何内容的函数(在这种情况下,
结果课非常慢)。 G++尝试过,并且
失败-在特定的用例中,它会中断。
(让它处理我忽略的其他问题不是
特别困难,但确实代表了很多方面
码。)

关于c++ - 在C++ 11中实现COW std::string的可能性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22147925/

相关文章:

c++ - 如何在Qt中使用fftw库

c++ - 解决 std::replace 编译错误

java - 获取字符串输入,将每个单词解析为全部小写并在一行上打印每个单词,非字母字符被视为单词之间的分隔符

string - 如何找到给定条件描述的所有字符串?

excel - VBA 中的 Unicode 字符串文字

c++ - 在 C++ 中使用的奇怪声明

c++ - 从指向 boost::any 的指针调用可变参数模板

C++ 为什么会输出错误的复利?

c++ - 执行不在线程之间切换 (c++11)

c++ - 在 linux 下的 GCC 中使用 std::thread 的正确链接选项是什么?