C++ では、const void *
関数の引数の型に a を使用する価値はありvoid *
ますか? a は不透明なので、ユーザーがを行う場合void *
以外に変更のリスクはありますか? 問題を回避するために特殊化を提供する共有ポインタ用のユーティリティ テンプレート クラスを使用していたが、特殊化が提供されていなかったので、これは単なる見落としであったのか、それとも決して必要ではないのでしょうか?reinterpret_cast
const_cast
const void *
void
void &
const void
6 に答える
これは、他のポインター型で提供されるのと同じ利点を提供します。-ness を明示的にconst
キャストしない限り、指されているものを変更することはできません。const
インターフェイスでconst void*
は、渡すものは何でも読み取れるが書き込まれないというクライアント コードへのサインです。たとえば、次のstd::memcpy
ように宣言されています
void *memcpy(void *dest, const void *src, std::size_t count);
に読み書きすることsrc
を通知しdest
ます。もちろん、それが実際に C++ で実装された場合 (可能ですが、ありそうにありません)、両方のポインターを他の型にキャストする必要があります。
これで「何の得にもならない」と感じたら、それは明らかに価値のないconst
キーワードそのものです。
memcpy
は、2 つのポインター パラメーター (onevoid*
と other ) を取りますconst void*
。const char*
2 番目のパラメーターは(または他の const オブジェクト型へのポインター) 引数から暗黙的に変換できますが、最初のパラメーターは変換できません。
暗黙的な変換が存在しないことが値です。これにより、ユーザーは、意図的に const を破棄するのではなく、必要な (ありそうもない) イベントで意図的に const を破棄するようになります。
次にmemcpy
、または同様の関数の実装内で、プログラマーは、その参照先を変更しようとする前に、パラメーターをconst_cast
C スタイルでキャストする必要があります。const 以外のパラメーターを使用して、そのリファランドを変更できますconst void*
。static_cast
あなたが書く必要があるキャストの種類は、うまくいけば、あなたがやっていることは賢明であるかどうかについて何かを教えてくれます.
あなたの shared_ptr ヘルパー関数がvoid
特別に扱う必要がある場合、cv 修飾されたすべてを特別に扱う必要があると思いますvoid
。つまり、 、 、 、 の 4 つvoid
のconst void
ケースvolatile void
ですconst volatile void
。しかし、関数のユーザーが過去に で試してみて、shared_ptr<void>
うまくいかなかったと不満を述べたが、 で試したことがない場合shared_ptr<const void>
、問題は発生していない可能性があります。
たぶんshared_ptr<void>
、それが現れていないほど十分に異常です。誰かが最終的に正しい型を復元するときはいつでも、正しい修飾子も復元することに基づいて、を使用するような人はshared_ptr<void>
cv 修飾子を捨てることを気にしない傾向があるかもしれません。
考えてみてください-shared_ptr<const void>
まったく機能しますか、それともデリータを呼び出すコードでからへshared_ptr
の暗黙的な変換が必要ですか? を使用したことがあるかどうかは覚えていません。T*
void*
shared_ptr<const T>
の「ドキュメンテーション値」を忘れないでくださいconst
。誰かがいつでもそれを捨てることができますが、const
は、指している対象がポインターを介して変更されるべきではないという本来の意図を示す役割を果たします。 const_cast
(さらにreinterpret_cast
言えば)常に注意して使用する必要があり、必要な場合はプログラマーを一時停止する必要があります。
はい、const
常にあるのと同じ利点 (のいくつか) があります:コンテンツが変更されるべきではないという事実を文書化します。
次のコードを想像してください。
int const object = some_value();
some_function(&object);
この呼び出しは、関数の引数が として宣言されている場合にのみコンパイルされvoid const*
ます。それ以外の場合、クライアントはconst_cast
constness をキャストするために が必要になります。もちろん、クライアントにこの不便さを感じてほしくないし、(constness を捨てて) データについて嘘をつくことも望んでいません。
すべての用途と同様に、const
2 つの目的を果たします。関数の実装では、コンパイラが誤用を検出するのに役立ちます。これは、言及したように、const_cast
(またはCスタイルのキャスト)によって強制および沈黙させることができます。
しかしconst
、2 番目の目的を果たします。オブジェクトが変更されないという約束を提供し、そうすることで、ユーザーが const オブジェクトへのポインターを渡すことができるようになり (約束を守るという前提で)、関数を効果的に幅広く使用できるようになります。これは、次の簡単な例で確認できます。
void foo( const void* );
void bar( void* );
int main() {
const int value = 10;
foo( &value ); // correct, the function promises not to modify the value
//bar( &value ); // error, this would break const correctness
}
コードを「自己文書化」することにはまだ利点があります。
store_pod(const void* data, std::size_t bytes);
コメントなしで指定すると、ポイント先のデータが変更されないことがわかります。
また、その約束を破るには、関数が aと a のconst
両方を実行する必要があることに注意してください。const_cast
reinterpret_cast