私が使用しているサードパーティライブラリでは、ポインタをvoid*
として渡す必要があります。いつでも、このポインタは継承チェーンのいくつかのクラスのうちの1つにキャストする必要があるかもしれません。これは多重継承が関与しているときに失敗するので、これを安全に行うための私の考案した方法は、クラス型をポインタと共に伝播して、目的の型に適切にキャストすることです。複数のクラスタイプ間で安全にキャストするテンプレート関数を使用する
#include <assert.h>
#include <stdint.h>
#include <cstddef>
#include <stdio.h>
enum class kTypeFlag { PARENTONE, PARENTTWO, CHILD };
#define CAST_TO_CLASS_TYPE(TO, FROM, POINTER) \
dynamic_cast<TO*>(static_cast<FROM*>(POINTER))
class ParentOne {
public:
ParentOne() { }
virtual ~ParentOne() { }
virtual size_t byte_size() const = 0;
};
class ParentTwo {
public:
ParentTwo(uint32_t id) : id_(id) { }
virtual ~ParentTwo() { }
uint32_t id() const { return id_; }
private:
const uint32_t id_;
};
class Child : public ParentOne, public ParentTwo {
public:
Child(uint32_t id) : ParentOne(), ParentTwo(id) { }
virtual ~Child() { }
size_t byte_size() const override { return sizeof(*this); }
};
template <class T>
T* Convert(void* pointer, kTypeFlag flag) {
switch (flag) {
case kTypeFlag::PARENTONE:
return CAST_TO_CLASS_TYPE(T, ParentOne, pointer);
case kTypeFlag::PARENTTWO:
return CAST_TO_CLASS_TYPE(T, ParentTwo, pointer);
case kTypeFlag::CHILD:
return CAST_TO_CLASS_TYPE(T, Child, pointer);
default:
assert(0 && "invalid flag to Convert");
}
}
int main() {
Child* child = new Child(5);
printf("byte_size: %zu\n", child->byte_size());
printf("id: %u\n", child->id());
ParentOne* p1 = Convert<ParentOne>(child, kTypeFlag::CHILD);
ParentTwo* p2 = Convert<ParentTwo>(child, kTypeFlag::CHILD);
printf("byte_size: %zu\n", p1->byte_size());
printf("id: %u\n", p2->id());
}
注CAST_TO_CLASS_TYPE
ではなくstatic_cast
のdynamic_cast
を使用していること以下は、意図した目標を示しています。 static_cast
を使用している場合は、ビルドが失敗するとこれは次のとおりです。
run.cc:40:14: error: static_cast from 'ParentTwo *' to 'ParentOne *', which are not related by inheritance, is not allowed
return CAST_TO_CLASS_TYPE(T, ParentTwo, pointer);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
run.cc:9:3: note: expanded from macro 'CAST_TO_CLASS_TYPE'
static_cast<TO*>(static_cast<FROM*>(POINTER))
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
run.cc:53:19: note: in instantiation of function template specialization 'Convert<ParentOne>' requested here
ParentOne* p1 = Convert<ParentOne>(child, kTypeFlag::CHILD);
^
だから私の質問は以下のとおりです。static_cast
を使用しているとき、なぜビルドが失敗するのか?あるいはこれを完全に行うための別の方法がありますか?最終的な目標は、void*
をいくつかの異なるタイプにキャストする必要があるために、いくつかの型の安全を強化することです。私はこれがどのように行われるか心配していません。上記は単に私が考え出すことのできる最高のものです。
EDIT:dynamic_cast
が理想的でない理由を明確にすることができます。次のスニペットはうまくコンパイルされます:
ParentTwo* p2 = new ParentTwo(42);
ParentOne* p1 = Convert<ParentOne>(p2, kTypeFlag::PARENTTWO);
printf("id: %u\n", p2->id());
printf("byte_size: %zu\n", p1->byte_size());
実行時に中断します。これはコンパイル時に捕捉されることが好ましい。だから私はstatic_cast
を使いたいのですが、上記の理由から可能ではありません。
なぜあなたは物事が複雑になり過ぎています。単純に 'ParentOne * p1 = child'を使わないのはなぜですか? –
あなたのプログラムは完全に私には似ています。どのようなコンパイラとフラグを使用していますか? –
@MohitJainそのコードはデモンストレーション用です。 'child'が' void * 'に代入されると、親クラスにのみアクセスすることができます。 –