【问题标题】:Are there any reasons why the StringPiece/StringRef idiom is not more popular?StringPiece/StringRef 习语不受欢迎有什么原因吗?
【发布时间】:2010-01-31 18:13:28
【问题描述】:

来自StringPiece class in Chromium's source code的文档:

// A string-like object that points to a sized piece of memory.
//
// Functions or methods may use const StringPiece& parameters to accept either
// a "const char*" or a "string" value that will be implicitly converted to
// a StringPiece.  
//
// Systematic usage of StringPiece is encouraged as it will reduce unnecessary
// conversions from "const char*" to "string" and back again.

使用示例:

void foo(StringPiece const & str) // Pass by ref. is probably not needed
{
   // str has same interface of const std::string
}

int main()
{
    string bar("bar");
    foo(bar); // OK, no mem. alloc.

    // No mem. alloc. either, would be if arg. of "foo" was std::string
    foo("baz");  
}

这似乎是一个如此重要且明显的优化,以至于我无法理解为什么它没有得到更广泛的应用,以及为什么类似于 StringPiece 的类还没有在标准中。

有什么理由不应该用这个类替换我自己代码中stringchar* 参数的使用? C++ 标准库中是否已经有类似的东西?

更新。我了解到 LLVM 的源代码使用了类似的概念:StringRef 类。

【问题讨论】:

标签: c++ string


【解决方案1】:

稍晚一点,但是...

StringPiece 背后的想法非常好。该类可以捕获std::stringconst char * 并将它们传递给函数。这是一个例子:

void process(const StringRef s){
   // do something
}

process("Hello"); // const char *
std::string s = "Hello";
process(s); // std::string
process(std::string("Hello")); // std::string rvalue

如果函数接受std::string,如果您传递const char * 并复制整个字符(例如memcpy),您实际上创建了一个临时对象。

您也不必担心 livetime,因为您将 StringRef 传递给函数/方法。

有这样的类:

  • Google - StringPiece

  • boost - boost::string_ref

  • LLVM - 字符串引用

我自己的(不完整的)实现可以在这里看到:
https://github.com/nmmmnu/HM3/blob/master/include/stringref.h

2016 年更新:

在 C++17 中,有std::string_view。我没有详细研究它,但总的来说它具有相同的想法。也与我的实现类似,它具有constexpr c-tor,因此您可以在编译时创建对象并将其与其他constexpr 函数一起使用。

【讨论】:

    【解决方案2】:

    这个问题已经得到了很好的回答,但是为了提供更多背景信息,StringPiece 模式在 Google 内部非常广泛地使用并且已经使用了很多年。在 Google 编码指南中强烈建议使用它,这几乎肯定是 Chrome(以及随后的 Chromium)采用它的原因。

    【讨论】:

      【解决方案3】:

      StringPiece 保留一个指向传递给其构造函数的字符串数据的指针。因此,它依赖于该字符串的生命周期比 StringPiece 长。如果您使用 StringPiece 作为函数参数,则可以保证这一点;例如,如果您在类成员中保留字符串的副本,则不是这样。

      它不像 std::string 那样通用,而且绝对不能替代它。

      【讨论】:

      • 是的,您永远不应该保留它的副本。我认为这可以通过制作它的副本来轻松执行。和 op = 私人的。至于不是 std::string 的通用替代品,我同意。两者都旨在一起工作。 std::string => 通用; StringPiece => 仅作为只读函数参数
      • 我不允许赋值,只是因为我要求类上的所有方法都声明为 const,从而强调这是一个 const 对象。我认为成对声明复制 ctor 和分配的做法是不允许复制 ctor 的唯一原因。
      • @jmucchiello - 并且还防止人们存储自己的字符串副本并使其存活时间比函数长。我认为除非您禁用复制 ctor,否则您无法阻止这种情况。
      【解决方案4】:

      标准试图完全摆脱 const char* 以支持字符串,因此添加更多转换选项是无用的。

      还要注意,一个格式良好的程序应该使用 string 或 const char* ;)。

      【讨论】:

      • @Kornel Kisielewicz:但是如果它节省了内存分配,它怎么会没用,如果字符串很长,这可能会很昂贵?我认为 C++ 是关于“不要为不使用的东西付费”。
      • @Manuel - 它违反了 DRY 原则,并有由此而来的所有陷阱
      • 摆脱 const char* 永远不会发生。字符串常量在某些情况下仍然适用,将它们复制到字符串对象是浪费的。此外,虽然将 const char* 常量作为全局对象使用 std::string 做同样的事情是微不足道的,但在棘手和不可能之间。
      【解决方案5】:

      StringPiece 很棒,但片段不是以 null 结尾的。如果您需要传递到采用空终止字符串的较低级别接口,则需要将字符串复制到空终止字符串中。

      (不是每个人都购买了 STL 或 std::string。)

      【讨论】:

        【解决方案6】:

        因为何必呢?通过复制省略和/或按引用传递,通常也可以避免为std::string 分配内存。

        C++ 中的字符串情况已经够混乱了,没有添加更多的字符串类。

        如果要从头开始重新设计语言,或者如果向后兼容性不是问题,那么这是可以对 C++ 中的字符串处理进行的许多可能改进之一。但是现在我们被 char*std::string 困住了,在混合中添加一个 stringref 样式的类会引起很多混乱,而且收益有限。

        除此之外,用一对迭代器不是更惯用地实现相同的效果吗? 如果我想传递一个字符序列,无论它们属于字符串还是char*,为什么不只使用一对迭代器来分隔它们?

        【讨论】:

        • 但是如果你走“两个迭代器”的路线,那么你必须发明一个类似于 StringPiece 的类,它为你提供了一个方便的类似字符串的接口。函数的第一行总是类似于“StringRange str(str_beg, str_end);”。无论如何都被接受,因为它提供了迄今为止最好的评估。
        • 关于方便的类似字符串的接口是正确的,但请记住,迭代器在 C++ 中已经是惯用的。我不明白为什么您的函数需要从迭代器创建一个字符串范围。相反,整个字符串操作接口应该作为迭代器上的免费函数一开始就提供。 Boost 的 StringAlgo 库修复了大部分问题。
        • 我认为您不会真正喜欢您的建议。想象一下像 strA+strB+strC 这样简单的迭代器。
        • 我从未说过这是一个理想的解决方案。 ;) 但是更好的解决方案是添加一个 general Range 对象,它不仅可以用于字符串,还可以用于任何类型的范围。
        • 参见 Boost.Range 库。及其未来的替代品 Boost.RangeEx。我梦想有一天他们会成为标准。
        猜你喜欢
        • 2019-10-17
        • 2015-07-29
        • 2016-01-20
        • 2011-07-05
        • 2021-04-04
        • 1970-01-01
        • 2011-07-10
        • 2011-06-15
        • 2015-05-23
        相关资源
        最近更新 更多