首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >意外的std::当返回对此的引用时,ostream输出

意外的std::当返回对此的引用时,ostream输出
EN

Stack Overflow用户
提问于 2013-12-09 22:00:23
回答 2查看 89关注 0票数 0

我肯定是错过了一些非常基本的东西。

我有一个简单的类,执行“就地”操作,然后返回对this的引用(这允许我链接不同的操作)。当我打印到一个std::ostream时,我会看到一个意想不到的输出。下面的代码将帮助我解释。

代码语言:javascript
复制
#include<iostream>

struct Container {

  Container(int x, int y, int z)
      : m_data{x, y, z} { }

  int operator()(size_t index) const {
    return m_data[index];
  }

  Container& shift() {
    int tmp = m_data[0];
    m_data[0] = m_data[1];
    m_data[1] = m_data[2];
    m_data[2] = tmp;
    return *this;
  }

  int m_data[3];
};

std::ostream& operator<<(std::ostream& os, const Container& c) {
  os<<"["<<c(0)<<", "<<c(1)<<", "<<c(2)<<"]";
  return os;  
}


int main() {
  std::cout<<std::endl<<"Behaviour Line-By-Line (works as expected)"<<std::endl;

  Container d(1, 2, 3);

  std::cout<<"Container as built:               "<<d<<std::endl;

  d.shift();  
  std::cout<<"Container after first shift:      "<<d<<std::endl;

  d.shift();
  std::cout<<"Container after second shift:     "<<d<<std::endl;

  std::cout<<std::endl<<"Behaviour On The Same Line (not as expected)"<<std::endl;

  Container c(1, 2, 3);

  std::cout<<"Container as built:               "<<c<<std::endl
           <<"Container after first shift:      "<<c.shift()<<std::endl
           <<"Container after second shift:     "<<c.shift()<<std::endl;


  return 0;
}

编译(OS 10.7.4使用GCC 4.8.1)并运行:

代码语言:javascript
复制
$ g++ example.cpp -std=c++11 -Wall -Wextra
$ ./a.out

Behaviour Line-By-Line (works as expected)
Container as built:               [1, 2, 3]
Container after first shift:      [2, 3, 1]
Container after second shift:     [3, 1, 2]

Behaviour On The Same Line (not as expected)
Container as built:               [3, 1, 2]
Container after first shift:      [3, 1, 2]
Container after second shift:     [3, 1, 2]

如您所见,当我将修改操作放在与operator<<相同的行上时,输出似乎会缓冲更改(因为缺少更好的单词)。

我的问题是:

为什么会发生这种情况,以及如何使“同一行”行为与“逐行行为”相匹配。

谢谢!

编辑:

根据@KeithSmith的建议,我将Container::shift修改为:

代码语言:javascript
复制
  Container& shift() {
    std::cout<<"shifting... "<<std::flush;
    int tmp = m_data[0];
    m_data[0] = m_data[1];
    m_data[1] = m_data[2];
    m_data[2] = tmp;
    return *this;
  }

得到了输出:

代码语言:javascript
复制
Behaviour Line-By-Line (works as expected)
Container as built:               [1, 2, 3]
shifting... Container after first shift:      [2, 3, 1]
shifting... Container after second shift:     [3, 1, 2]

Behaviour On The Same Line (not as expected)
shifting... shifting... Container as built:               [3, 1, 2]
Container after first shift:      [3, 1, 2]
Container after second shift:     [3, 1, 2]

正如在几个答案中所解释的,操作的顺序没有定义。在“不像预期的”情况下,转移发生在流之前,但我想它可能以任何顺序发生。

我的带走:非常,非常小心,当inlining手术有副作用!我想我早该知道的!可惜我没有!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-12-09 22:05:48

第二个cout大致转换为operator <<(operator <<(operator <<(cout, c), c.shift(), c.shift()),并且由于参数计算顺序未指定,所有的转移都可以在开始时发生。

第一个;中的cout引入了序列点,这确保了评估顺序。有关序列点的更多信息,请参见这个:Undefined behavior and sequence points

票数 1
EN

Stack Overflow用户

发布于 2013-12-09 22:06:50

未指定计算函数参数的顺序。如果表达式有副作用,并且取决于它们的顺序,则需要确保按顺序执行。也就是说,这些声明

代码语言:javascript
复制
std::cout<<"Container as built:               "<<c<<std::endl
         <<"Container after first shift:      "<<c.shift()<<std::endl
         <<"Container after second shift:     "<<c.shift()<<std::endl;

可以处理为

  • 首先评估一个c.shift()
  • 第二次评估另一个c.shift()
  • 现在它已经对所有对象进行了评估并开始输出它们。

它还可以以不同的顺序计算表达式。只对shift操作符的函数调用进行排序,显然,如果它依赖于子表达式,则需要首先计算子表达式。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20481729

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档