提问者:小点点

C++中的“字符串插值”:构造一个带有嵌入值的std::字符串(例如,对于错误消息)?


我想创建一个带有嵌入信息的字符串。 实现我想要的目标的一种方法(不是唯一的方法)叫做字符串插值或变量替换,其中字符串中的占位符被替换为实际值。

在C语言中,我会这样做:

printf("error! value was %d but I expected %d",actualValue,expectedValue)

而如果我是用python编程,我会做这样的事情:

"error! value was {0} but I expected {1}".format(actualValue,expectedValue)

这两个例子都是字符串插值。

我如何在C++中做到这一点?

重要注意事项:

  1. 我知道,如果我想将这样的消息打印到标准输出(不是字符串插值,而是打印出我想要的字符串类型),我可以使用std::cout:
cout << "error! value was " << actualValue << " but I expected "
<< expectedValue;

我不想将字符串打印到stdout。 我想将一个std::string作为参数传递给一个函数(例如异常对象的构造函数)。

编辑

对于我的直接用法,我不关心性能(我正在为crying‘out loud引发一个异常!)。 然而,了解各种方法的相对性能通常非常非常有用。

为什么不直接使用printf本身(C++毕竟是C的超集……)呢? 这个答案讨论了一些为什么不这样做的原因。 据我所知,类型安全是一个很大的原因:如果您放入%d,那么您放入的变量最好是可以转换为整数的,因为函数就是这样计算出它是什么类型的。 使用要插入的变量的实际类型的编译时知识的方法要安全得多。


共3个答案

匿名用户

方法1:使用字符串流

看起来std::stringstream提供了一个快速解决方案:

std::stringstream ss;
ss << "error! value was " << actualValue << " but I expected " <<  expectedValue << endl;

//example usage
throw MyException(ss.str())

阳性

  • 无外部依赖
  • 我相信这在C++03和C++11中都能工作。

阴性

  • 据说相当慢
  • 更复杂一点:您必须创建一个流,向其写入,然后从中提取字符串。

方法二:Boost格式

Boost格式库也是一种可能。 使用它,您可以做到:

throw MyException(boost::format("error! value was %1% but I expected %2%") % actualValue % expectedValue);

阳性

  • 与stringstream方法相比相当干净:一个紧凑的构造

阴性

  • 据说相当慢:在内部使用stream方法
  • 它是一个外部依赖项

编辑:

方法3:可变模板参数

printf的类型安全版本似乎可以通过使用可变模板参数(用于模板参数数目不定的技术术语)来创建。 我在这方面看到了一些可能性:

  • 这个问题给出了一个紧凑的例子,并用那个例子讨论了性能问题。
  • 这个问题的答案,其实现也相当紧凑,但据报道仍然存在性能问题。
  • 本答案中讨论的fmt库据说速度相当快,并且似乎与printf本身一样干净,但它是一个外部依赖项

阳性

  • 用法简单:只需调用类似printf的函数
  • 据报道fmt库速度相当快
  • 其他选项似乎相当紧凑(不需要外部依赖关系)

阴性

  • fmt库虽然是fast,但它是一个外部依赖项
  • 其他选项显然存在一些性能问题

匿名用户

在C++20中,您将能够使用std::formater

已接受的论文见http://www.open-std.org/jtc1/sc22/WG21/docs/papers/2019/p0645r9.html。

这将支持python样式的格式设置:

string s1 = std::format("{1} to {0}", "a", "b");

已经有一个可用的实现:https://github.com/fmtlib/fmt

匿名用户

在C++11中,您可以使用std::to_string:

"error! value was " + std::to_string(actualValue) + " but I expected " + std::to_string(expectedValue)

它并不漂亮,但很简单,你可以用一个宏来缩小它。 性能不是很好,因为您事先没有保留()空间。 可变模板可能会更快,看起来更好。

这种字符串构造(而不是插值)也不利于本地化,但如果需要,您可能会使用库。

相关问题


MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(c++|中|字符串|插|值|构造|带有|嵌入|值|std|字符串)' ORDER BY qid DESC LIMIT 20
MySQL Error : Got error 'repetition-operator operand invalid' from regexp
MySQL Errno : 1139
Message : Got error 'repetition-operator operand invalid' from regexp
Need Help?