【问题标题】:Why doesn't std::string_view have assign() and clear() methods?为什么 std::string_view 没有 assign() 和 clear() 方法?
【发布时间】:2019-01-24 11:53:36
【问题描述】:

这些方法的实现对我来说似乎很简单,它们将使std::stringstd::string_view 的使用更具互换性。毕竟,std::string_view 具有使对象处于与这些方法相同的状态的构造函数。可以像这样解决缺少的方法:

std::string s {"abcd"};
std::string_view v {s.c_str()};
std::cout << "ctor:   " << v << std::endl; // "abcd"
v = {s.c_str() + 1, 2};
std::cout << "assign: " << v << std::endl; // "bc"
v = {nullptr}; // or even v = {};
std::cout << "clear:  " << v << std::endl; // ""

那么,标准中没有包括这两种明显的方法的原因是什么?

更新: 您的 cmets 中的一个普遍问题似乎是“有什么意义?”,所以我会给您一些背景信息。我正在解析一个大字符串,结果是子字符串的结构。该结果结构是字符串视图的自然候选者,因此我不必复制所有那些甚至重叠的字符串。结果的一部分是到字符串视图的映射,因此我可能需要在获取键时将它们构造为空,并在稍后获取值时填充它们。在解析时,我需要跟踪中间字符串,这涉及更新和重置它们。现在它们也可以被字符串视图替换,这就是我在那些缺失的函数上发生的事情。当然,我可以继续使用字符串或用普通的旧 ptr-ptr 或 ptr-size 对替换它们,但这正是 std::string_view 的用途,对吧?

【问题讨论】:

  • 要明确:您会期望这些使对象“查看”不同的字符串(实际上并未修改当前查看的字符串)?
  • 是的,只是改变视图,而不是底层字符串。
  • 这在语义上与相应的 std::string 方法不同(这可能是您正在寻找的原因)。就像更改指向 nullptr 的指针与删除指向的对象不同。
  • Afaik,string_view 的主要目的是替换函数参数中的 const string&。这是一个很好的问题,string_view::clear 和 string_view::assign 的意义是什么。
  • @Simpleton 不,那个向量类比不成立。您删除(并取消分配)所有元素。 intint* 都是微不足道的释放,没有语义差异。作为记录,我同意你的观点,assignclear 的“解释”没有明显的语义差异。但是您必须同意,clear 是否也清除查看的字符串本身可能会造成混淆 - 毕竟std::string 会这样做。

标签: c++ stl string-view


【解决方案1】:

std::string 接口因其blown API 而名声不佳,这就是为什么std::string_view 不太可能获得像std::string 一样多的方法,仅仅因为它方便或使这两种类型更易于互换。

但更重要的是,这些类型并不意味着可以互换。 “清除”字符容器上的视图是什么意思?由于 clear() 存在于所有 STL 容器中并且做了一些有意义的事情,所以拥有 std::string_view::clear() 会很混乱。

此外,某些数据的视图仅供临时使用,例如一个只读函数参数。你为什么要分配给它呢?这是一个使用std::string_view的示例函数签名:

// Called e.g. with "x86_64-Darwin-16.7.0"
std::string_view extractOSName(std::string_view configStr)
{
    // Parse input, return a new view. Lifetime/ownership managed by caller.
    // No need to re-assign anything, let alone "clearing" them.
}

【讨论】:

  • 我同意std::string API 完全被夸大了,但assign()clear() 函数在所有容器中几乎都是标准的。并且std::string_view 充当容器,因为您查看std::string_viewstd::string const &amp; 并不重要。这就是我所说的可互换。但我不明白std::string_view::clear() 有什么令人困惑的地方。除了清除视图,它还能做什么,所以之后它是空的?没有人会期望它会真正删除底层字符串,对吧?
  • 您说“看std::string_viewstd::string const &amp; 并不重要”,但这并不意味着assign 没有任何意义首先,因为不能修改std::string const&amp;?关于clear 主题:我认为视图是主要用于函数参数的短期对象。重新设置他们查看的对象可能根本不是他们设计的场景?但这只是猜测。
  • 我想必须区分构造字符串视图和使用它。构造可能不像单个构造函数调用那么简单,请参阅我在问题中提供的上下文。还有std::string_view::remove_prefix/suffix() 函数,它们是非常量的。但我认为使用std::string_view const &amp; 应该与使用std::string const &amp; 几乎没有区别。
【解决方案2】:

这只是真正的猜测,但普遍的共识似乎是这些操作不太清楚。

我个人认为“清除视图”非常有意义(我们也不要忘记 remove_prefixremove_suffix 存在!虽然见下文...),但我也同意还有其他解释,这可能是常见的,这没有多大意义。回想一下,string_view 旨在补充const std::string&amp;,而不是std::string,而且您命名的函数都不是std::string 常量接口的一部分。

说实话,我们完全需要这种对话的事实本身可能是一个很好的理由,一开始就没有这个功能。

来自the final proposal for string_view,以下段落不是专门针对assignclear,而是作为委员会对此主题的相关观点[lol]

s/remove_prefix/pop_front/, etc.

在 Kona 2012 中,我提出了一个带有 pop_front 等成员的 range&lt;&gt; 类来调整范围的边界。那里的讨论表明,委员会成员对轻量级范围操作与容器操作使用相同的名称感到不舒服。现有做法不同意此操作的名称,因此我保留了 Google 的 StringPiece 使用的名称。

这个提议实际上确实包含了一个clear(),它被毫不客气地从注册中删除了in a later, isolated, rationale-starved proposal

现在,有人可能会争辩说,这些函数可以因此以不同的名称提供,但这从未被提议过,而且很难想象有什么替代名称可以解决这个问题,而不仅仅是操作的坏名称。

由于我们可以很容易地分配一个新的string_view,包括一个空的,所以整个问题都解决了,根本不用费心去解决它。

【讨论】:

  • 关于remove_prefixremove_suffix 的信息很好。该提案还解决了它存在的原因,即使它不在字符串中(据说是实用程序),但没有说明为什么如果将它添加到视图中,为什么不将它也添加到字符串中。
  • “整个问题只需不去解决它就可以解决”好吧,我只是对不包括这些功能的理由感到好奇。我希望有人能加入标准中的适当段落,而不是人们表达他们(对我来说毫无道理的)对这些假设功能的不适。它就在那里,它说了什么?他们省略了它们,因为一些“委员会成员对此感到不舒服”。哦,好吧...看到这里的讨论可能是有道理的...标记为已回答。
  • @Simpleton 澄清一下,是委员会通过不解决问题解决了问题,只是不添加这些功能。这是一个公平的问题。我只是不认为我们能比上面做得更好。当然,规范性或非规范性标准文本对此没有任何理由。
  • @LightnessRacesinOrbit,这是最终提案吗?那好像有清楚的?
  • @JeffGarrett 哦,伙计,确实如此......(a)有人对我撒谎,(b)情节变厚了。我必须在早上重新审视这个。好地方!
【解决方案3】:

这些方法的实现对我来说似乎很简单,它们会使std::stringstd::string_view 的使用更容易互换。

std::string_view 不能替代std::string。它旨在替代const std::string&amp;assignclear 不是您可以调用的 const std::string&amp; 的成员函数。

【讨论】:

  • 好点,但remove_prefixremove_suffix都不是
  • @LightnessRacesinOrbit 并且它们也不存在于 std::string 中,语义不同。
  • 也许assignclear 可能存在于自然string_view::reset 重载中。这将与可选项、智能指针等一致。
【解决方案4】:

string_view 对于纯 C 字符串(char 数组)来说可能是很棒的字符串接口包装器,当后备存储不在您的控制范围内时,但您仍然想在不控制的情况下访问/修改内容干预后备存储的能力(或需要)(即在动态内存分配不存在问题的情况下)。

【讨论】:

    【解决方案5】:

    std::string_views 是对内存中其他地方的字符串的引用。但与好的(或坏的)旧 C char * 不同,std::string_views 有一个指针和一个大小。这就是为什么它们可以引用其他字符串的特定部分,这就是为什么remove_prefix()remove_suffix()string_views 上是有意义的操作。

    但是,std::string_view 不需要 assign() 方法,因为这与赋值运算符没有什么不同。 std::string 有一个 assign() 方法来处理很多情况,std::string 的赋值运算符无法处理,因为它不能接受多个参数。几个例子:

    std::string A { "This is a somewhat lengthy string" };
    std::list<char> L { '1', 'A', 'Z' };
    std::string B;
    
    B.assign(80, ' ');               // Create an empty line of spaces
    B.assign(A, 10, 8);              // Copy out a substring of another string
    B.assign(L.begin(), L.end());    // Assign from an STL container
    

    在这三种情况中,只有中间一种对std::string_view 有意义。第一种情况和最后一种情况都需要分配一个字符串,而这是std::string_view 做不到的。

    不过,使用std::string_view::substr() 可以轻松实现中间情况。例如:

    std::string A { "This is a somewhat lengthy string" };
    std::string_view V { "Directly pointing to A C string" };
    std::string_view B;
    
    B = std::string_view{A}.substr(10, 8);    // Reference substring of A
    B = B.substr(0, 4);                       // Further shorten the reference
    B = V.substr(0, 8);                       // Reassign with another reference
    B = B.substr(0, 0);                       // "Clear" the string_view
    

    所以clear()assign()std::string_view 都可以使用赋值运算符来完成。 std::string 不是这样:中间赋值 B.assign(A, 10, 8); 用来自 A 的数据覆盖已分配的 B。使用像B = A.substr(10, 8); 这样的赋值运算符的解决方案将导致通过substr() 调用创建一个临时的std::string,然后是内部释放内存,之前分配给B。这通常效率不高,这就是将子字符串分配方法添加到std::string() 的原因。而std::string_view::substr()不需要复制任何字符串数据,所以B = std::string_view{A}.substr(10, 8);的赋值很容易优化!

    请注意,以下代码是错误的,并且会在堆上留下对临时子字符串的悬空引用:

    std::string A { "This is a somewhat lengthy string" };
    std::string_view V { A.substr(10, 8) };
    

    因此,如果您需要将std::string_view 设置为字符串的子字符串,请始终先转换为std::string_view(通过std::string_view(string)),然后在std::string_view 上执行所需的substr() 方法调用.当然,请确保原始 string 比新生成的 std::string_view 寿命更长,否则您最终会得到悬空引用。

    【讨论】:

    • 使用 substr() 的解决方法很好,但你是在代表我争论。如果 assign() 和 clear() 可以用 substr() 来表示,为什么不把它们放在首位呢?另外,我猜 remove_prefix()/remove_suffix() 可以用类似的方式表达。
    猜你喜欢
    • 1970-01-01
    • 2018-09-22
    • 1970-01-01
    • 2022-11-17
    • 2017-11-22
    • 1970-01-01
    • 2022-01-03
    • 1970-01-01
    相关资源
    最近更新 更多