【问题标题】:Why std::string concatenation operator works like right-associative one?为什么 std::string 连接运算符像右关联运算符一样工作?
【发布时间】:2014-09-15 07:33:19
【问题描述】:

运行以下从我的宠物项目中提取并使用 GCC 4.9.1(以及 4.8.1)编译的 MWE

#include <iostream>
#include <string>
#include <sstream>

class InputStringStream
{
public:
    InputStringStream(const std::string& str) : istringstream(str), currentLine() {}
    std::string readLine()
    {
        std::getline(istringstream, currentLine);
        return currentLine;
    }

private:
    std::istringstream istringstream;
    std::string currentLine;
};

int main()
{
    std::string s = std::string("line1\nline2\nline3");
    InputStringStream stream(s);
    std::cout << stream.readLine() + "\n" + stream.readLine() + "\n" + stream.readLine() << std::endl;
    return 0;
}

产生以下输出

line3
line2
line1

在我期待的时候

line1
line2
line3

我做错了什么?

附:使用 Apple LLVM 编译器 5.1 版编译的相同代码产生了我所期望的结果。 Visual C++ 2012 在 GCC 方面。

【问题讨论】:

  • 只是为了重申这一点:您的问题本身错误地混淆了两个不相关的问题:运算符的关联性决定了表达式的 含义(如果您will),但您遇到的问题是关于子表达式的求值顺序。两者没有任何关系。
  • @KerrekSB,是的,现在我明白了。但是当我问这个问题时,我(错误地)认为问题是关于 operator+() 的关联性。所以我会保持原样,它可能会帮助其他人。

标签: c++ string gcc visual-c++-2012


【解决方案1】:

函数参数的求值顺序是未指定,所以你做错的是持有错误的、无根据的信念和期望。 (+&lt;&lt; 等重载运算符只是普通的函数调用。)

您必须以确定的顺序提取流元素,并且有责任这样做。例如:

std::cout << stream.readLine() + '\n';
std::cout << stream.readLine() + '\n';
std::cout << stream.readLine() + '\n';

更好的是,避免冗余和临时字符串:

for (auto i : { 1, 2, 3 }) { std::cout << stream.readLine() << '\n'; }

【讨论】:

  • 知道了!多年来没有用 C++ 编写,几乎忘记了所有内容...... :(
  • for(int i = 0; i &lt; 3; ++i)相比,那个for循环太性感了
  • @iFreilicht 真的吗?当我需要一个好的 ol' 时,我更喜欢一个好的 ol'。
  • @MSalters:我正在考虑从算术转换到几何的功能,而不是缺陷!
  • @iFreilicht #define loop(n) { int __m = n; for (var i = 0; i &lt; __m; ++i) 你有它:P。 (对不起,对不起)
【解决方案2】:

问题不在于关联性,在这个表达式中:

stream.readLine() + "\n" + stream.readLine() + "\n" + stream.readLine() 

未指定首先调用哪个stream.readLine()

【讨论】:

  • “序列点”本身并不是真正的问题。 readline 调用没有混合。一个在另一个开始之前完成。在 C++11 中,术语,一个调用在另一个之前被排序。真正的问题是不确定哪个先行。
【解决方案3】:

您做错的唯一一件事是假设在您调用 stream.readLine() 三次的表达式中,这些表达式的出现顺序与调用顺序匹配。一些编译器可能会先评估最后一个调用,有些可能会按顺序评估它们。理论上,有些人甚至可能首先评估中间的。这只是 C++ 的一般规则:表达式的求值顺序是未指定的。

在所有实现上获得相同结果的一种简单方法是将三个结果存储在单独的变量中。

【讨论】:

  • 一个编译器有时会先计算一个表达式,有时会先计算另一个。不仅适用于外观相同的代码,而且适用于相同的代码。
  • @gasher729 当然。当前实现的一个现实场景是,如果它发生在一个内联函数中,根据调用者的不同,一个参数有时可能更容易先评估,有时更难。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-30
  • 1970-01-01
  • 2014-12-10
  • 1970-01-01
  • 2011-03-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多