【问题标题】:Can you assign a substring of a std::string to itself?您可以将 std::string 的子字符串分配给自身吗?
【发布时间】:2015-03-24 09:17:44
【问题描述】:

我最近发现需要将std::string 的内容替换为其自身的子字符串。我认为在这里调用的最合乎逻辑的函数如下,来自http://www.cplusplus.com/reference/string/string/assign/

子字符串 (2)      string& assign (const string& str, size_t subpos, size_t sublen);

复制从字符位置 subpos 开始并跨越 sublen 个字符的 str 部分(或者直到 str 的结尾,如果 str 太短或 sublen 是 string::npos)。

     str
另一个字符串对象,其值被复制或移动。

     subpos
str 中作为子字符串复制到对象的第一个字符的位置。如果这大于 str 的长度,则抛出 out_of_range。注意:str 中的第一个字符由值 0(不是 1)表示。

     sublen
要复制的子字符串的长度(如果字符串较短,则复制尽可能多的字符)。 string::npos 的值表示直到 str 结尾的所有字符。

但是,我不确定这是否允许,或者它是否会损坏字符串数据。例如,我知道memcpy() 不允许(或至少不保证在这种情况下不会损坏)用自身的(一部分)覆盖内存区域(请参阅memcpy() vs memmove())。但是不知道上面的方法有没有同样的限制。

更一般地说,如果我应该能够自己找出这个问题的答案,你能评论一下吗?我链接到的文档中没有任何内容可以让我清楚地知道这个问题的答案是什么,除了 也许 str 参数描述中的限定符“Another”(“另一个 字符串对象”),这似乎暗示它不能是 this 对象,尽管我不认为这是明确的。这是文档中的弱点吗?

【问题讨论】:

  • Assignable C++ 类通常以安全的方式实现复制赋值运算符(即检查分配*this = *this)。标准容器类也不例外。 然而, 即使这不是必需的——字符串的子字符串不再是字符串本身。同样,“Cplusplus.com”网站的措辞似乎很糟糕——cppreference.com 使用了“替换”这个词,从中很明显你所做的事情应该是安全的。
  • @TheParamagneticCroissant,为什么“替换”这个词清楚地表明它应该是安全的?同样,您可以说memcpy() 替换了目标缓冲区的内容,但这并不意味着它是安全的。
  • @bgoldst 我很确定标准没有完全涵盖这种情况,因此很难处理。如果您想保持安全,请使用 substr 和赋值运算符(即使用副本)。
  • @dyp 不幸的是,这还不够。也许这应该成为 EWG 问题的主题。
  • @Columbo 我怀疑它最终可能会像LWG 526

标签: c++ string


【解决方案1】:

没有。

这个操作是由[string::assign]/4定义的:

basic_string& assign(const basic_string& str, size_type pos,
    size_type n = npos);

效果:确定要分配的字符串的有效长度rlen 作为nstr.size() - pos 中较小的一个并调用assign(str.data() + pos rlen)

(dat typo)

然后:

basic_string& assign(const charT* s, size_type n);

效果:将*this控制的字符串替换为字符串 长度为n,其元素是s指向的元素的副本。

这并没有说明str.assign(str, 0) 是否安全(特别是,我们无法知道何时每个字符的副本会发生!)。

因此我强烈建议你避免这样做。

【讨论】:

  • 对于vector::push_back 等,有人认为省略任何要求可以保证其行为良好(不允许失败)。 libstdc++ 和 libc++ 似乎支持显式分配别名字符串。
  • @dyp:我看不出一个需求的遗漏是如何隐含地引入一个需求的!好吧,我这样做的原因是,如果没有注意到先决条件,那么人们可能会认为没有先决条件。但这都是猜测,我更愿意得出结论,除非特别解决这个问题,否则它在定义上是不安全的。
  • @dyp 在我阅读了您的链接后正要说。我相信只要论据合理,调用应该没问题。
  • @Columbo,将字符串的一部分分配给自身时,它总是重叠,因为您将字符串的一部分分配给自身。
  • 我认为规范很明确。 “替换字符串...其元素是 s 所指向的元素的副本” - 这意味着实现是需要肯定有这个效果。没有先决条件提及重叠输入。因此,使用重叠输入调用它必须是安全的,因为实现必须具有所描述的效果
【解决方案2】:

不要尝试。

它可能有效,但正如所选答案中所建议的那样,不能确保它是安全的。在最好的情况下,根据您的实现,将创建一个临时对象,然后将其销毁。

一种模拟方法,它不会创建临时对象并且确实比调用assignsubstr 更快:

void trimTo(string & s, size_t pos = 0, size_t len = string::npos)
{ 
    s.erase(pos + len); 
    s.erase(pos, len); 
}

那么,

trimTo(myString, fromPos, numChars);

作为

myString.assign(myString.substr(fromPos, numChars);

但它至少快两倍。

【讨论】:

    【解决方案3】:

    显然,这与 operator= 的决定相同(防止自赋值)。

    _Myt& assign(const _Myt& _Right,
        size_type _Roff, size_type _Count = npos)
        {   // assign _Right [_Roff, _Roff + _Count)
        _Right._Check_offset(_Roff);
        _Count = _Right._Clamp_suffix_size(_Roff, _Count);
    
        if (this == &_Right)
            erase((size_type)(_Roff + _Count)), erase(0, _Roff);    // substring
        else if (_Grow(_Count))
            {   // make room and assign new stuff
            _Traits::copy(this->_Myptr(),
                _Right._Myptr() + _Roff, _Count);
            _Eos(_Count);
            }
        return (*this);
        }
    

    【讨论】:

    • 很高兴看到您引用源代码的任何实现都可以防止这种情况发生。它是什么实现?什么版本?它随着时间而改变吗?其他实现怎么说?标准是怎么说的? 那个会随着时间而改变吗?
    • 代码 sn-p 取自 VS 2015,所以我猜任何比它更新的 MS STL 代码都有。
    • 从那时起就有了新的标准;这不是一个安全的假设。
    猜你喜欢
    • 1970-01-01
    • 2015-07-11
    • 1970-01-01
    • 2022-06-29
    • 1970-01-01
    • 2014-08-22
    • 2020-11-04
    • 2011-07-17
    • 2012-07-21
    相关资源
    最近更新 更多