4

以下のコードは悪い習慣ですか、それとも未定義の動作ですか?基本的に、可変としてマークされていないメンバーを変更するためにconstfuncを呼び出しています。デモへのリンク

この質問(彼の質問はC ++型システムを破壊しますか? )を刺激したMehrdadの功績と、デモのマイナーな改善のためのdavidの功績です。

#include <iostream>
using namespace std;

struct BreakConst
{
    int v;
    int *p;
    BreakConst() { v = 0; p = &v; } 
    void break_stuff() const { ++*p; }
};
void f(const BreakConst& bc) {
    bc.break_stuff();
}

ほとんどの回答が基づいている元のバージョン:

DavidRodríguez、jpalecek、Mehrdadによる回答
はい:これは「未定義動作」です

int main()
{
    const BreakConst bc;
    cout << bc.v << endl;   // 0
    bc.break_stuff();       // O:)
    cout << bc.v << endl;   // 1

    return 0;
}

新しい代替質問:

Mehrdadによる回答
いいえ:これは「未定義動作」ではありません

int main()
{
    BreakConst bc;
    cout << bc.v << endl;   // 0
    f(bc);                  // O:)
    cout << bc.v << endl;   // 1

    return 0;
}

結果:

0
1
4

3 に答える 3

3

この場合、それは未定義の動作だと思います。bcconstオブジェクトであるため、そのすべてのサブオブジェクト(mutables未満)1も同様であるbc.v必要があり、オブジェクトの変更はconst、達成されたとしてもUB2です

[1] C ++ 03 3.9.3 / 3:

const修飾クラスオブジェクトの各非静的、非可変、非参照データメンバーはconst修飾されています。

[2] C ++ 03 7.1.5.1/4:

可変(7.1.1)と宣言されたクラスメンバーを変更できることを除いて、constオブジェクトをその存続期間(3.8)中に変更しようとすると、未定義の動作が発生します。

質問の編集に応答する編集:いいえ、コードの変更されたバージョンは未定義の動作を引き起こしません。それは悪い習慣かもしれませんが、実際には時々役立つかもしれません。あなたは例えばすることができます。これを使用して、const-iterators(DRY)を介してクラスにイテレーターを実装します。

class const_iterator
{
public:
  const T& dereference() const; // complicated
};

class iterator : public const_iterator
{
public:
  T& dereference() const { return const_cast<T&>(const_iterator::dereference()); }
};

もちろん、これはiteratorsが可変コンテナからのみ作成できること、constバージョンとnon-constバージョンに違いがない(COWなどがない)などの事実に依存していますが、それはかなり一般的です。

于 2012-06-18T21:35:55.127 に答える
3

特定のケースでは、オブジェクト参照だけでなくconstであるため、未定義の動作です。次の場合は、悪い習慣になります(そして、危険なほど未定義動作に近づきます)。

void f( const BreakConst& b ) {
   bc.break_stuff();
}
int main() {
   BreakConst b;
   f( b );
}

違いは、この場合、レベルがの参照であっても、実際のオブジェクトはconstではないことfです。未定義動作に危険なほど近いのは、const-nessをキャストするメンバー関数が、呼び出されたオブジェクトがconstであるかどうかを認識できない可能性があるため、すべての制御を失ったためです。

于 2012-06-18T21:37:18.227 に答える
2

編集後:

いいえ、未定義ではありません。const参照を介して可変オブジェクトを変更できます。それは完全に許可されており、合法です。


編集前:

はい、それは未定義でなければなりません。なぜなら、標準(私はここでドラフトを見ています)が明確に言っているからです:

§7.1.6.1.4

宣言されたクラスメンバー(§77.1.1)を変更できることを除いて、オブジェクトの存続期間中(§3.8)にオブジェクトmutableを変更しようとすると、未定義の動作が発生します。const

そうです-メンバーは変更可能ではないので、それを変更することは明らかに未定義の動作です。

なぜルールがそのようになっているの、これが意図的なものなのか、それが本当に抜け穴なのか、別のルールにも違反しているのか、それを見ただけでどのように伝えるべきかなどはわかりませんが、それがUBであるかどうかの問題:はい、それは標準によれば未定義です。

于 2012-06-18T21:45:43.283 に答える