一般的なルールとして、C++コードでconst_cast<>()
を使用すると、(ほとんどの場合)設計の欠陥が明らかになるため、これは悪い習慣と見なされることがよくあります。
私はこれに完全に同意しますが、const_cast<>()
を使用しているケースはokであり、唯一の解決策であるのではないかと思います。
あなたが知っている/あなたが遭遇したいくつかの例を教えてください。
どうもありがとうございました。
const_cast
は、この(論争の的となっている)記事で実践されているように、volatile
修飾子を削除するためにも使用されます。
これは、constが正しくないレガシーAPIでのみ使用するように設計されています。つまり、const以外のインターフェースを持つが、実際にはインターフェース上で何も変更しない、変更できない関数を使用します。
他の人が言っているように、その主な目的は、引数を変更しないことがわかっている非constの正しい関数に渡すために、オブジェクトからconst
を削除することです。
コードの重複を回避するためのトリック(マイヤーズによる?)があり、次のようになります。
struct foo
{
const return_type& get(void) const
{
// fancy pants code that you definitely
// don't want to repeat
return theValue; // and got it
}
return_type& get(void)
{
// well-defined: Add const to *this,
// call the const version, then
// const-cast to remove const (because
// *this is non-const, this is ok)
return const_cast<return_type&>(static_cast<const foo&>(*this).get());
}
};
通常の使用は「設計上の欠陥」を隠す必要があるためであるというあなたの声明に同意します。
IMEの典型的な使用シナリオの1つは、C++を既存のCコードにインターフェースしようとする場合です。既存のCコードの多くは、文字列が変更されていない場合でもC文字列をchar *
として受け取りますが、通常、C++ではconst char *
に変換されるものとして表されます。これは、通常const_castを使用して解決する2つの言語間のインピーダンスの不一致です。もちろん、veryインターフェースしているコードが、渡されるデータの変更についてかわいいアイデアを取得しないようにすることをお勧めします。
新しく書かれたコードでは臭いがするコードだと思いますが、古いCおよびC++コードとのインターフェースには、必要悪です。とは言うものの、POD以外のオブジェクトにconst_cast
を必要とするコードは通常、コードレベルではなく設計レベルで解決する必要があるため、非常に警戒します。
(私の意見では)正当な使用法の1つは、std::set
イテレータを使用することです。セットで使用されるキーの変更を防ぐために、これらは常にconst
です。キーを変更すると、セットの内部構造が壊れ、未定義の動作が発生します。
ただし、キーが変更されない限り、オブジェクト内の他のデータを変更しても安全です。
次のようなstd::set
があるとします。
std::set<MyObject> some_set;
そしてこのようなクラス:
class MyObject {
public:
MyObject(const std::string &key)
: key_(key) {}
bool operator<(const MyObject &other) const {
return key_ < other.key_;
}
private:
// ...
// <some other data>
// ...
const std::string key_;
};
上記の例では、キーはすでにconstであるため、オブジェクトを変更しても、セットの内部構造を壊すことはできません。
通常、設定されたイテレータからはconst
参照のみを取得できます。
const MyObject &object = *some_set_iterator;
ただし、キーはconst
であるため、逆参照されたイテレータをconst_cast
しても安全です。
MyObject &object = const_cast<MyObject &>(*some_set_iterator);
これの非常に正当な使用法の1つは、constとnon const api(それぞれconstオブジェクトとnon constオブジェクト)の両方がある場合です。
class Bar {
const SomeType& foo() const;
SomeType& foo();
}
次に、両方の関数でコードを複製したくないので、よく使用します
class Bar {
SomeType& foo() {
//Actual implementation
}
const SomeType& foo() const {
return const_cast<Bar*>(this)->foo();
}
};
もちろん、これはfooがconstセマンティクスに違反することを行わないことを前提としています。
C++入門書(第5版)の本にconst_castの使用例があります。以下の関数は、const文字列への参照を返します
// return a reference to the shorter of two strings
const string &shorterString(const string &s1, const string
&s2)
{
return s1.size() <= s2.size() ? s1 : s2;
}
次に、この本では、const以外の参照が必要な場合について説明しています。
非const文字列引数のペアで関数を呼び出すことはできますが、結果としてconst文字列への参照を取得します。 nonconst引数が与えられたときに、単純な参照を生成するバージョンのshorterStringが必要になる場合があります。 const_castを使用して、このバージョンの関数を記述できます。
string &shorterString(string &s1, string &s2)
{
auto &r = shorterString(const_cast<const string&>(s1),
const_cast<const string&>(s2));
return const_cast<string&>(r);
}
このバージョンは、引数をconstへの参照にキャストすることにより、shorterStringのconstバージョンを呼び出します。この関数は、const文字列への参照を返します。これは、元の非const引数の1つにバインドされていることがわかっています。したがって、その文字列をプレーンな文字列&にキャストして戻すのが安全であることがわかります。
この本によると、キャストしても安全であることがわかっている場合に使用する必要があります。
はい、もちろん、変更できず、constが正しくない呼び出しコードの場合。データが変更されないことが確実にわかっている関数の呼び出しでのみ使用する必要があることに注意してください。