C++11 でということになるとちょっと面倒ですね。 C++17 で std::variant
や std::any
が定義されたわけなんですが、逆に言えばそれより前はまわりくどいことをせざるを得ませんでした。
動的型を実現する基本的な方法は抽象クラスです。 この場合は option1_t
や option2_t
が共通する抽象クラスを継承し、その抽象クラス経由で扱うことで異なる型を C++ の型システムの枠組みで安全に扱えるようになっています。 かなり雑にですがコードで書くとすれば以下のような要領でしょう。
cpp
1 # include <iostream>
2 # include <memory>
3 # include <string>
4 # include <typeinfo>
5
6 struct option_base {
7 virtual const std :: type_info & get_type ( void ) {
8 return typeid ( * this ) ;
9 }
10 } ;
11
12 struct option1_t : option_base {
13 option1_t ( std :: string name , int default_value , int min , int max )
14 : name ( name ) , default_value ( default_value ) , min ( min ) , max ( max ) { }
15 std :: string name ;
16 int default_value ;
17 int min ;
18 int max ;
19 } ;
20
21 struct option2_t : option_base {
22 option2_t ( std :: string name , int default_value , int min , int max )
23 : name ( name ) , default_value ( default_value ) , min ( min ) , max ( max ) { }
24 std :: string name ;
25 int default_value ;
26 int min ;
27 int max ;
28 } ;
29
30 struct target_common { } ;
31
32 struct table_t {
33 target_common * tat ;
34 const std :: unique_ptr < option_base > option ;
35 } ;
36
37 target_common data1 , data2 , data3 ;
38
39 const table_t table [ ] = {
40 { & data1 , std :: unique_ptr < option_base > ( new option1_t ( "foo" , 0 , - 1 , 1 ) ) } ,
41 { & data2 , std :: unique_ptr < option_base > ( new option2_t ( "bar" , 3 , 4 , 5 ) ) } } ;
42
43 int main ( void ) {
44 if ( table [ 0 ] . option -> get_type ( ) == typeid ( option1_t ) ) {
45 std :: cout << "table[0] is option1" << std :: endl
46 << "name is " << dynamic_cast < option1_t * > ( table [ 0 ] . option . get ( ) ) -> name << std :: endl ;
47 }
48
49 if ( table [ 0 ] . option -> get_type ( ) != typeid ( option2_t ) ) {
50 std :: cout << "table[0] is not option2 " << std :: endl ;
51 }
52 }
もし質問者が抽象クラスや実行時型情報の扱いをご存じでないのであればその詳細はここで説明するのは手に余るので適当な資料で学んでください。
ちなみに union
で質問者が失敗しているのは、供用体は実行時型情報を持たないので解体するときにどのメンバのデストラクタを使えばよいかわからないため色々と制限が付いているからです。 メンバのコンストラクタがトリビアルでなければならならず、そうでないときは供用体のデストラクタをユーザが適切に書けばよいのですが本来の型を知る方法がないので普通は使いません。 (使われることが少ないのであまり知られていませんが供用体はコンストラクタ・デストラクタを含めてメンバ関数を持つことができます。)