2010-04-21 173 views
1

我在一行中使用流操作符< <和位移操作符< <。 我有点困惑,为什么代码A)不会产生与代码B相同的输出)?operator <<:std :: cout << i <<(i << 1);

A)

int i = 4; 
std::cout << i << " " << (i << 1) << std::endl; //4 8 

B)

myint m = 4; 
std::cout << m << " " << (m << 1) << std::endl; //8 8 

类敏:

class myint { 
    int i; 
public: 
    myint(int ii) { 
     i = ii; 
    } 
    inline myint operator <<(int n){ 
     i = i << n; 
     return *this; 
    } 
    inline operator int(){ 
     return i; 
    } 
}; 

在此先感谢
哎呀

+0

几乎重复的:http://stackoverflow.com/questions/2603312/the-result-of-int-c0- coutcc /。对于大多数实际用途来说,它们是相同的,尽管使用“++”而不是“<<”作为修改操作符。 – 2010-04-21 21:19:35

+2

@Jerry Coffin:它们非常相似,但有整个“vs < <混淆和事实上<<通常不会改变它的论点 – 2010-04-21 21:23:50

回答

8

你的第二个例子是未定义的行为。

您已将myint类中的<<运算符定义为<<=。当您执行i << 1时,i中的值未被修改,但在执行m << 1时,m中的值为已修改。

在C++中,对于没有中间顺序点的变量(对于它们的参数而言,哪些函数调用和操作符不是这样)读取和写入(或写入多次)是未定义的行为。它是不确定的代码是否

std::cout << m << " " << (m << 1) << std::endl; 

将输出第一m之前或之后mm << 1更新。事实上,你的代码可能会做一些奇怪的事情,或者崩溃。未定义的行为可能导致字面上的任何事情,所以避免它。

一个适当的方法来定义<<运营商myint是:

myint operator<< (int n) const 
{ 
    return myint(this->i << n); 
} 

(该this->不是绝对必要的,只是我的风格,当我重载运算符)

+1

但是重载操作符是函数调用,有许多序列点。 – 2010-04-21 21:20:15

+2

'std :: cout << m <<“”<<(m << 1)'相当于'std :: cout.operator <<(m).operator <<(“”).operator <<( m.operator <<(1))'在每个最外层的操作符被调用时,有一个序列点,但是在传递给操作符的参数的评估之间没有序列点,包括内部调用换句话说,在'f(a()).g(b())'中,在调用'f'和调用'g''之间存在一个序列点。 ,但是调用'a'和'b'的顺序是不确定的。 – 2010-04-21 21:23:14

+0

也许你应该更新你的答案,因为没有你的评论中的解释,很明显,许多顺序点都不能保证关键表达式之间没有一个。 – 2010-04-21 21:29:56

2

你< <运营商其实是一个运营商。如果您更换

std::cout << i << " " << (i <<= 1) << std::endl; //8 8 

行,你应该得到8 8

+6

你已经强调了这个问题,但你的“答案”有未定义的行为 – 2010-04-21 21:18:32

1

嘛(M < < 1)M前进行评估,因而,M拥有8已经如您在操作< <你覆盖你自己的价值。

这是你身边的错误行为,运营商< <应该是const并且不能改变你的对象。

5

因为int < < X返回一个新的int。 myint < < X修改当前的myint。你的myint < <运营商应该固定做前者。

你第一次得到8的原因很明显是你的实现中首先调用1。实现可以以任意顺序自由执行。

0

因为<<运营商myint修改了它的lhs。因此,在评估m << 1后,m实际上将具有值8(而i << 1仅返回8,但不会使我等于8)。由于没有指定m<<1是否在cout << m之前执行(因为未指定函数或操作符的参数以何种顺序进行评估),因此未指定输出是8 8还是4 8

2

因为mmyInt你的第二个例子可以写成:

std::cout << m << " " << (m.operator<<(1)) << std::endl; 

评价的子表达式的顺序m(m.operator<<(1))是不确定的,所以没有说法,其中“m”你会得到在m中使用的第1个表达式(这是一个简单的m表达式)。所以你可能会得到“4 8”的结果,或者你可能会得到“8 8”。

请注意,语句不会导致未定义的行为,因为在修改m和“读取”之间存在序列点(至少一个函数调用)。但是子表达式的评估顺序没有指定,所以编译器必须产生一个结果(它不会崩溃 - 至少不合法),但是没有说明应该产生两种可能的结果中的哪一种。

因此,该声明与未定义行为一样有用,也就是说它不是非常有用。

0

C++语言没有定义操作符评估的顺序。它只定义它们的关联性。

由于您的结果取决于何时在表达式中计算了operator<<函数,因此结果未定义。

代数operator $功能应始终const并返回一个新的对象:

inline myint operator <<(int n) const { // ensure that "this" doesn't change 
    return i << n; // implicit conversion: call myint::myint(int) 
} 
相关问题