Vector<Base*> を期待する関数に Vector<Derived*> を取得する
-
02-07-2019 - |
質問
これらのクラスについて考えてみましょう。
class Base
{
...
};
class Derived : public Base
{
...
};
この機能
void BaseFoo( std::vector<Base*>vec )
{
...
}
そして最後に私のベクトル
std::vector<Derived*>derived;
合格したい derived
機能する BaseFoo
, 、しかしコンパイラはそれを許可しません。ベクトル全体をコピーせずにこれを解決するにはどうすればよいですか? std::vector<Base*>
?
解決
vector<Base*>
そして vector<Derived*>
は無関係なタイプであるため、これを行うことはできません。これについては、C++ FAQ で説明されています。 ここ.
変数を次から変更する必要があります vector<Derived*>
に vector<Base*>
そして挿入します Derived
オブジェクトをその中に入れます。
また、コピーを避けるために、 vector
不必要に、値ではなく const-reference によって渡す必要があります。
void BaseFoo( const std::vector<Base*>& vec )
{
...
}
最後に、メモリ リークを回避し、コードを例外安全にするために、ヒープに割り当てられたオブジェクトを処理するように設計されたコンテナの使用を検討してください。
#include <boost/ptr_container/ptr_vector.hpp>
boost::ptr_vector<Base> vec;
あるいは、生のポインターを使用する代わりにスマート ポインターを保持するようにベクトルを変更します。
#include <memory>
std::vector< std::shared_ptr<Base*> > vec;
または
#include <boost/shared_ptr.hpp>
std::vector< boost::shared_ptr<Base*> > vec;
それぞれの場合において、 BaseFoo
それに応じて機能します。
他のヒント
コンテナオブジェクトを渡す代わりに(vector<>
)、渡してください begin
そして end
イテレータは残りの STL アルゴリズムと同様です。それらを受け取る関数はテンプレート化され、Derived* と Base* のどちらを渡しても問題ありません。
この問題は、変更可能なコンテナを持つプログラミング言語で発生します。変更可能なリンゴの袋を果物の袋として渡すことはできません。なぜなら、他の人がその果物の袋にレモンを入れていないという確信が持てないからです。その後、その果物の袋はリンゴの袋としての資格を失います。リンゴの袋が変更可能でない場合は、それを果物の袋として渡しても問題ありません。共分散/反分散を検索します。
1 つのオプションはテンプレートを使用することです
template<typename T>
void BaseFoo( const std::vector<T*>& vec)
{
...
}
欠点は、実装をヘッダーに含める必要があり、コードが少し肥大化することです。最終的には型ごとに異なる関数がインスタンス化されますが、コードは同じままです。ユースケースによっては、これは手っ取り早い解決策になります。
編集します。ここでテンプレートが必要な理由は、他の投稿者が指摘したように、無関係な型に対して同じコードを記述しようとしているためです。テンプレートを使用すると、これらの問題を正確に解決できます。また、const 参照を使用するように更新しました。また、コピーが必要ない場合は、ベクトルのような「重い」オブジェクトを const 参照で渡す必要があります (基本的には常にです)。
一般に、ベース ポインターのコンテナーから開始します。その逆はできません。
取る マット・プライスさん 上記の答えのように、関数で使用する型が事前にわかっている場合は、ヘッダー ファイルで関数テンプレートを宣言し、それらの型の明示的なインスタンス化を追加できます。
// BaseFoo.h
template<typename T>
void BaseFoo( const std::vector<T*>& vec);
// BaseFoo.cpp
template<typename T>
void BaseFoo( const std::vector<T*>& vec);
{
...
}
// Explicit instantiation means no need for definition in the header file.
template void BaseFoo<Base> ( const std::vector<Base*>& vec );
template void BaseFoo<Derived> ( const std::vector<Derived*>& vec );
サードパーティのライブラリを扱っていて、これが唯一の希望である場合は、次のようにすることができます。
BaseFoo (*reinterpret_cast<std::vector<Base *> *>(&derived));
それ以外の場合は、他の提案のいずれかを使用してコードを修正してください。
もし std::vector
あなたが求めているものをサポートしていれば、キャストを使用せずに C++ 型システムを破ることが可能になります (編集:ChrisN の C++ FAQ Lite へのリンクでは、同じ問題について説明しています)。
class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};
void pushStuff(std::vector<Base*>& vec) {
vec.push_back(new Derived2);
vec.push_back(new Base);
}
...
std::vector<Derived1*> vec;
pushStuff(vec); // Not legal
// Now vec contains a Derived2 and a Base!
あなた以来 BaseFoo()
関数はベクトルを値で受け取りますが、渡した元のベクトルを変更することはできないため、私が書いたことは不可能です。ただし、非定数参照を受け取り、使用する場合は、 reinterpret_cast<std::vector<Base*>&>()
あなたを渡すために std::vector<Derived*>
, を実行すると、期待する結果が得られず、プログラムがクラッシュする可能性があります。
Java配列のサポート 共変サブタイピング, これには Java が必要です。 配列に値を格納するたびに実行時の型チェックを行う. 。これも望ましくない。
これらは無関係なタイプです。それはできません。