7

アクセス関数で const 修飾子を使用していないプロジェクト内で C++ ライブラリの使用を実装しようとしています。これまで、すべてのコードで const を使用してきましたが、この新しいライブラリは 2 つの主な問題を引き起こしています。

  1. 引数が const 参照として渡される関数は、これらの引数がライブラリによって定義された型である場合、引数のアクセス関数を使用できません。

  2. ライブラリによって定義された型のメンバ オブジェクトを持つクラスは、const 関数内でこれらのオブジェクトのアクセス関数を使用できません。

この問題を克服する最善の方法は何ですか? 最も簡単な解決策は、コードから const の使用をすべて削除することですが、それを行うのは非常にイライラします。

追加情報:この場合、ソース コードにアクセスでき、アクセス関数が何も変更しないことがわかります。より一般的なケースにも興味があったため、この情報は省略しました。私のシナリオでは、const_cast進むべき道のようです

PS ライブラリ ライターは悪ではありません。彼が親切にオープンソース化したのは、もう少しラフで準備が整ったコードです。ライブラリを捨てて、他の人が指摘したように、より専門的なものを使用できます。ただし、この時間に制約のある小さなプロジェクトでは、このライブラリへのインターフェイスのシンプルさが最良の選択になりました。

4

5 に答える 5

5

ライブラリ内の関数が実際に何かを変更するかどうかを判断するのはどれくらい簡単ですか?

簡単にわかり、そうでない場合はconst_cast、const ポインター/非 const への参照を使用して、ライブラリ関数を呼び出すことができます。これを行うために、ライブラリ クラスの周りにラッパーをスローすることをお勧めします。これは面倒で冗長ですが、そのコードをクラスから取得します。このラッパーは、ライブラリ クラスを使用する方法で動作できるかどうかに応じて、いくつかの const アクセサーを追加するサブクラスになる可能性があります。

判断が難しい場合、または実際に変更が加えられている場合は、コード内で非 const インスタンスとライブラリ クラスへの参照を使用する必要があります。mutableタイプ(2)のものには役立ちますが、タイプ(1)のものについては、非定数引数を渡すだけで済みます。

それが難しい理由の例として、ライブラリの作成者が次のようなものを書いたとします。

struct Foo {
    size_t times_accessed;
    int value;
    int get() {
        ++times_accessed;
        return value;
    }
};

const_castconstインスタンスでFooを呼び出すと、未定義の動作が発生しますget()[*]。したがって、それが呼び出されたオブジェクトを本当に変更しないことを確認する必要があります。非 const インスタンスへの const 参照を取得しても、getの const インスタンスを作成しないようにすることで、これを少し軽減できます。Fooそうすれば、あなたとあなたconst_castに電話getするとき、少なくともUBを引き起こさない. 関数が変更しないと主張するオブジェクトのフィールドが変更され続けるため、コードが混乱する可能性があります。

[*] なぜ未定義の動作なのですか? constオブジェクトの値が有効なプログラムで決して変更されないことを言語が保証できるようにするためには、そうする必要があります。この保証により、コンパイラは有用なことを行うことができます。たとえば、static constオブジェクトを読み取り専用のデータ セクションに配置したり、既知の値を使用してコードを最適化したりできます。constまた、可視の初期化子を持つ整数オブジェクトがコンパイル時の定数であることも意味します。これは、配列のサイズまたはテンプレート引数として使用できるようにするために標準で使用されます。const オブジェクトを変更するのが UB でない場合、const オブジェクトは定数ではなく、次のことは不可能です。

#include <iostream>

struct Foo {
    int a;
    Foo(int a) : a(a) {}
};

void nobody_knows_what_this_does1(const int *p); // defined in another TU
void nobody_knows_what_this_does2(const int *p); // defined in another TU

int main() {
    const Foo f(1);
    Foo g(1);
    nobody_knows_what_this_does1(&f.a);
    nobody_knows_what_this_does2(&g.a);
    int x;
    if (std::cin >> x) {
        std::cout << (x / f.a); // Optimization opportunity!
        std::cout << (x / g.a); // Cannot optimize!
    }
}

fは const オブジェクトであり、したがっても const オブジェクトであるため、関数の最後で使用されるf.aと、オプティマイザーf.aは の値が 1 であることを認識します。選択した場合、除算を最適化することができます。:は const オブジェクトではなく、それへのポインターが不明なコードに渡されたため、その値が変更された可能性があります。したがって、あなたがorの作成者であり、それを使用して参照先を変更することを考えている場合、参照先が非定数であることを何らかの方法で知っている場合にのみ、それを行うことができます。通常は使用しないため、通常は使用しません。g.agnobody_knows_what_this_does1nobody_knows_what_this_does2const_castpconst_cast

于 2012-05-22T16:30:31.590 に答える
2

もう1つのオプションは、オブジェクトを変更可能な温度にコピーしてピッチすることです。クラスがコピーコンストラクターを提供し、それほど高価ではない状況にある場合、これはおそらく最も安全な方法です。これは、100%安全であることがわかっているので、利用可能な場合は私の好みの方法です。愚かな例:

int getInfoFromString(String& str); //what??  why isn't str const :(

私もです

 String temp(str);
 int stuffINeed = getInfoFromString(temp);
 //happy
于 2012-05-22T16:33:28.203 に答える
2

次のオプションがあると思います。

  1. 指定子を使用している場合にライブラリが正常に機能していると確信している場合は、ライブラリを処理するときにオブジェクトの const-ness を削除するためにconst使用できます。const_cast<>

    または、const オブジェクトの非 const コピーを作成してライブラリに渡し、元のオブジェクトの非 const 部分への変更を更新することもできます

  2. const 正しい別のライブラリを検索する

  3. コードからすべての const を削除します (非推奨)

于 2012-05-22T16:29:29.587 に答える
1

ライブラリのインターフェイスが大きくない場合は、ライブラリの関数に渡されるパラメータをキャストまたはコピーすることで、コードを期待される型に調整するラッパーを作成できます。

ただし、ライブラリを使用するためにコードを劣化させないでください。

于 2012-05-22T19:16:56.880 に答える
1

mutable別の提案:キーワードに精通していますか? 正しく使用すれば、コードに 1 つの単語を追加するだけで、実際に実行しようとしていることが正確に達成される可能性があります。このキーワードは のレベルで宗教的な意見を呼び起こす可能性がgotoあります。これは、実際には最良の設計オプションであるため、使用される 100 回ごとにおそらくクラッジとして使用されるためです。あなたの場合、それが何であるかは議論の余地がありますが、私はそれが精神に合っていると思います.あなたの迷惑なライブラリオブジェクトは、メソッドのセマンティックなconst-nessを壊すことなく偽の変更ができるものなので、先に進んでください.

class Outer {
    mutable Inner inner;
    public void foo() const { 
        inner.nonConstMethod(); //compiles because inner is mutable
    }
};
于 2012-05-22T17:04:31.187 に答える