草庐IT

c++ - 使用 C 样式字符串文字与构造未命名的 std::string 对象的默认建议?

coder 2023-11-13 原文

因此,C++ 14 引入了许多要使用的用户定义文字,其中之一是 "s" literal suffix , 用于创建 std::string对象。根据文档,其行为与构建 std::string 完全相同。对象,像这样:

auto str = "Hello World!"s; // RHS is equivalent to: std::string{ "Hello World!" }

当然构建一个未命名的std::string对象可以在 C++ 14 之前完成,但是因为 C++ 14 的方式要简单得多,我认为更多的人实际上会考虑构建 std::string现场的物体比以前多,这就是为什么我认为提出这个问题是有道理的。

所以我的问题很简单:在什么情况下,构造一个未命名的 std::string 是个好(或坏)主意对象,而不是简单地使用 C 风格的字符串文字?

示例 1:

考虑以下:
void foo(std::string arg);

foo("bar");  // option 1
foo("bar"s); // option 2

如果我是对的,第一个方法将调用适当的构造函数重载 std::string在内部创建一个对象 foo的作用域,第二种方法会先构造一个未命名的字符串对象,然后移动构造foo的论据。尽管我确信编译器非常擅长优化这样的东西,但是,与第一个替代方案相反,第二个版本似乎涉及额外的移动(当然,移动并不昂贵)。但同样,在使用合理的编译器编译后,最终结果很可能是高度优化的,并且无论如何都没有多余的移动/复制。

另外,如果 foo 被重载以接受右值引用怎么办?在这种情况下,我认为拨打 foo("bar"s) 是有意义的。 ,但我可能是错的。

示例 2:

考虑以下:
std::cout << "Hello World!" << std::endl;  // option 1
std::cout << "Hello World!"s << std::endl; // option 2

在这种情况下,std::string对象可能传递给 cout的运算符通过右值引用,第一个选项可能传递一个指针,所以两者都是非常便宜的操作,但第二个有先构造对象的额外成本。不过,这可能是一种更安全的方式(?)。

当然,在所有情况下,构建一个 std::string object 可能会导致堆分配,这可能会引发,因此也应考虑异常安全。 不过,这在第二个示例中更像是一个问题,就像在第一个示例中一样,std::string无论如何,在这两种情况下都会构造对象。实际上,从构造字符串对象中获得异常的可能性很小,但在某些情况下仍然可能是一个有效的参数。

如果您能想到更多要考虑的示例,请将它们包含在您的答案中。我对有关未命名 std::string 用法的一般建议很感兴趣。对象,而不仅仅是这两种特殊情况。我包括这些只是为了指出我对这个主题的一些想法。

另外,如果我做错了什么,请随时纠正我,因为我绝不是 C++ 专家。我描述的行为只是我对事物如何运作的猜测,我并没有真正基于实际研究或实验。

最佳答案

In what cases it's a good (or bad) idea construct an unnamed std::string object, instead of simply using a C-style string literal?


一个 std::string - 当你特别想要一个 std::string 类型的变量时,文字是一个好主意, 是否为
  • 稍后修改值( auto s = "123"s; s += '\n'; )
  • 更丰富、直观且不易出错的界面(值语义、迭代器、findsize 等)
  • 值语义意味着 == , <复制等对值起作用,这与 C 字符串文字衰减到 const char* 后的指针/按引用语义不同。 s

  • 调用 some_templated_function("123"s)将简洁地确保 <std::string>实例化,可以在内部使用值语义处理参数
  • 如果您知道其他代码正在为 std::string 实例化模板无论如何,并且相对于您的资源限制而言它具有显着的复杂性,您可能希望通过 std::string也是为了避免对 const char* 进行不必要的实例化也是,但很少需要关心

  • 包含嵌入的值 NUL

  • 在以下情况下可能更喜欢 C 风格的字符串文字:
  • 需要指针式语义(或至少不是问题)
  • 该值只会传递给期望 const char* 的函数无论如何,或者std::string无论如何,临时文件都会被构建,如果有可能重用相同的 std::string,你并不关心你给你的编译器优化器一个额外的障碍来实现编译或加载时间构建。实例(例如,当通过 const -reference 传递给函数时) - 同样很少需要关心。
  • (另一个罕见且令人讨厌的黑客)您以某种方式利用了编译器的字符串池行为,例如如果它保证对于任何给定的翻译单元 const char*如果文本不同,字符串文字只会(但当然总是)不同
  • 你不能真正从 std::string 得到同样的结果.data()/.c_str() ,因为在程序执行期间,相同的地址可能与不同的文本(以及不同的 std::string 实例)相关联,以及 std::string不同地址的缓冲区可能包含相同的文本

  • 您会受益于在 std::string 之后指针保持有效会离开作用域并被销毁(例如,给定 enum My_Enum { Zero, One }; - const char* str(My_Enum e) { return e == Zero ? "0" : "1"; } 是安全的,但 const char* str(My_Enum e) { return e == Zero ? "0"s.c_str() : "1"s.c_str(); } 不是,而 std::string str(My_Enum e) { return e == Zero ? "0"s : "1"s; } 总是使用动态分配(无 SSO,或更长的文本)有过早悲观的味道)
  • 您正在利用相邻 C 字符串文字的编译时连接(例如 "abc" "xyz" 成为一个连续的 const char[] 文字 "abcxyz" ) - 这在宏替换中特别有用
  • 您的内存受限和/或不想在动态内存分配期间冒异常或崩溃的风险

  • 讨论
    [basic.string.literals] 21.7 列表:

    string operator "" s(const char* str, size_t len);

    Returns: string{str,len}


    基本上,使用 ""s正在调用一个返回 std::string 的函数按值 - 至关重要的是,您可以绑定(bind) const引用或右值引用,但不是左值引用。
    用于拨打 void foo(std::string arg); 时, arg确实会是搬家建。

    Also, what if foo is overloaded to accept rvalue references? In that case, I think it would make sense to call foo("bar"s), but I could be wrong.


    你选择哪个并不重要。维护明智 - 如果 foo(const std::string&)曾经更改为 foo(const char*) , 仅 foo("xyz");调用将无缝地继续工作,但它可能有一些模糊合理的原因(所以 C 代码也可以调用它? - 但如果不继续为现有客户端代码提供 foo(const std::string&) 重载仍然有点疯狂; 所以它可以在 C 中实现? - 也许;消除对 <string> header 的依赖? - 与现代计算资源无关)。

    std::cout << "Hello World!" << std::endl; // option 1

    std::cout << "Hello World!"s << std::endl; // option 2


    前者会打电话operator<<(std::ostream&, const char*) ,直接访问常量字符串文字数据,唯一的缺点是流媒体可能必须扫描终止的 NUL。 “选项 2”将匹配 const -reference 重载并暗示构造一个临时对象,尽管编译器可能能够对其进行优化,因此他们不会经常这样做,甚至在编译时有效地创建字符串对象(这可能仅适用于足够短的字符串以使用对象内短字符串优化 (SSO) 方法)。如果他们还没有进行这样的优化,那么潜在的好处以及这样做的压力/愿望可能会增加。

    关于c++ - 使用 C 样式字符串文字与构造未命名的 std::string 对象的默认建议?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32116430/

    有关c++ - 使用 C 样式字符串文字与构造未命名的 std::string 对象的默认建议?的更多相关文章

    1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

      我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

    2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    3. Ruby 解析字符串 - 2

      我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

    4. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

      我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

    5. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    6. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

      很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

    7. ruby - 在 Ruby 中使用匿名模块 - 2

      假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

    8. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

      我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

    9. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

      我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

    10. ruby-on-rails - unicode 字符串的长度 - 2

      在我的Rails(2.3,Ruby1.8.7)应用程序中,我需要将字符串截断到一定长度。该字符串是unicode,在控制台中运行测试时,例如'א'.length,我意识到返回了双倍长度。我想要一个与编码无关的长度,以便对unicode字符串或latin1编码字符串进行相同的截断。我已经了解了Ruby的大部分unicode资料,但仍然有些一头雾水。应该如何解决这个问题? 最佳答案 Rails有一个返回多字节字符的mb_chars方法。试试unicode_string.mb_chars.slice(0,50)

    随机推荐