2011-10-27 6 views
6

私は数日前に見た、小さくてかわいらしい問題です。私の友人にインタビューで尋ねました。整数整数型と全く同じように動作するC++整数クラスを作成する

最初のインタビューの質問は、「次のコードの出力はどうなりますか?」

int i = 2; 
i = i++ + i++; 

正解である((2 + 2)+ 1)+ 1 = 6、即ちポストインクリメントを二回割り当て前に、しかし添加後に適用されます。

次に、オペレータが実行される正確な順序をログに記録するために、1つの整数とオーバーロード演算子+()と演算子++(int)を持つ単純なクラスを作成したかったのです。

これは私が得たものである:

class A 
{ 
public: 
A(int _data) : data(_data) { } 

A &operator=(const A& _rhs) 
{ 
    data = _rhs.data; 
    cout<<" -- assign: "<<data<<endl; 
} 

A operator++(int _unused) 
{ 
    A _tmp = data; 
    data++; 

    cout<<" -- post-increment: "<<data<<endl; 
    return _tmp; 
} 

A operator+(const A &_rhs) 
{ 
    A _tmp = data + _rhs.data; 

    cout<<" -- addition: "<<data<<"+"<<_rhs.data<<endl; 
    return _tmp; 
} 

inline operator int() const { return data; } 

private: 
    int data; 
}; 

結果はかなり落胆した次のようなあまり洗練された構造物については、

-- post-increment: 3 
-- post-increment: 4 
-- addition: 3+2 
-- assign: 5 

(A _dt2 = A ++;)、それはそれが必要として動作しかし、演算子の実行順序は整数型と同じではありません。

それは私が推測する、コンパイラ固有の問題かもしれません:

$ gcc --version 
gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3 
Copyright (C) 2009 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 

だから、私は失われたビットよ:)

+6

私は最初の出力が定義されていないことを確信しています –

+2

はい、最初のスニペットは未定義の動作をしています。 – sharptooth

+2

[未定義の動作とシーケンスポイント]の重複可能性があります(http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points) –

答えて

14

初期の面接の質問だった:「の出力がどうなりますか次のコード?

int i = 2; 
i = i++ + i++; 

あなたが間のシーケンスポイントなしで同じ変数を複数回変更しているので、正しい答えは、undefined behaviourです。

C++ 03標準§5 [expr] p4

指摘し、個々の事業者や個々の表現の部分式のオペランドの評価のため、副作用が行われる順番は、指定されていない場合を除き。

これはあなたの本当の質問に答えることはできませんが、あなたが、整数のようなクラスを作成し、operator++(int)operator+(A const&)をオーバーロードしても同様であろう。関数への引数の評価の順序は指定されていません。コンパイラが好きな順番で実行することができます。結果は不定です。

+0

両方の演算子がオーバーロードされている場合、ビヘイビアーは未定義ではなくなりました。各演算子は関数呼び出しになり、シーケンスポイントが導入されます。あなたが言うように、結果はまだ不明です。 –

+0

@Mike:あなたのコメントを削除して新しいコメントを作成しないでください。 :Pしかし、ええ、あなたは正しい、演算子の機能呼び出しになる喜び。 – Xeo

+0

注記のある箇所を除いて、標準からの見積もり、** C++ 03セクション5:表現、パラ4:** *を追加することもできます。 &&および||の特別な規則、個々の演算子のオペランドの評価の順序、個々の式の部分式、および副作用の順序は「未指定」です。** –

1

実際、あなたはかなり早い段階でエラーが発生しました。

最初のインタビューの質問は、「次のコードの出力はどうなりますか?」

int i = 2; 
i = i++ + i++; 

この質問に対する正しい答えは、出力が定義されていない「です。「とりなしシーケンスポイントなしで変数を変更して読み取ることにより

、あなたは未定義の動作を起動されている。


具体的には、この場合にはどのようなお尻であなたを噛むことは順序がするためのパラメータということです+オペレータが評価不定されている;これは、いくつかの注目すべき例外すなわち、短絡論理演算子を用いて、オペレータおよび機能の両方の一般的な場合に真である

4

正解は((2 + 2です。 )+ 1)+ 1 = 6、すなわちポストincr割り当ては割り当ての前に2回適用されますが、追加後に適用されます。指摘し、個々の事業者や個々の表現の部分式のオペランドの評価のため、副作用が行われる順番は、指定されていない場合を除いて

:正しい答えではありません

。前のシーケンスポイントと次のシーケンスポイントの間では、スカラーオブジェクトは、最大でも1回の式の評価によってストアされた値を変更しなければならない。さらに、以前の値は、格納される値を決定するためにのみアクセスされるものとする。この段落の要件は、完全な 式の部分式の許容可能な順序ごとに満たされなければならない。それ以外の場合、動作は未定義です。 - 「整数整数型と全く同じ行動するC++整数クラスを作成します」 - 私が指している必要があり、単独であなたの質問のタイトルを取る: -
別に他の人がすでに指摘したものとISO-IEC-14882

7

全く別の理由があるのはなぜですか不可です。

はありませんショートカットの動作をエミュレートするには||および& &オペランドの両側、すなわちオペランドの両側にある演算子は、何が評価されても評価されます。

編集:のコメントをチェックしてください。 「私の知識には」十分ではないようです。しかし、Steve Jessopは、全体的なポイントを有効にする別の例を持っています。

これは完全にあなたの増分の質問に関係のない、しかし、あなたの質問のタイトルへの局所的であるので、私はそれが言及されるべきであると思いました。

+0

+1、良い点。 – Xeo

+3

しかし、整数型をエミュレートする場合は、これらの演算子を再実装する必要はないと思います。演算子 '&&'と '||'はクラス '' operator bool() 'を自動的に呼び出すことができます。その場合、それらの短絡動作は保存されます。 –

+0

それは不可能ですが、それは本当です。より強力な理由は、暗黙的にクラスAのインスタンスを(例えば) 'long'に変換し、暗黙の変換で許可される1つのユーザ定義変換を「使い切り」し、' int'を 'long'に変換しない理由't。したがって、 'long 'をとるコンストラクタを持つ別のクラス' B'があり、 'int i = 0;変換チェーンA - > int - > long - > Bには2つのユーザー定義関数が含まれているので、 'B a = i;'、 'B b =定義済みのコンバージョン –

関連する問題