現: 2021-08-08 (日) 22:45:20 takatsuka[3] [4] | |||
---|---|---|---|
Line 1: | Line 1: | ||
+ | 変数の型によって処理を切り替えたいことが稀によくあるんじゃないかと思います。 | ||
+ | C++ では RTTI なる情報で実行時に型を判断出来るので以下のようなコードが書けます。 | ||
+ | |||
+ | #prettify{{ | ||
+ | struct Animal { | ||
+ | virtual ~Animal() {} // ポリモーフィックにする | ||
+ | }; | ||
+ | struct Dog : public Animal {}; | ||
+ | struct Cat : public Animal {}; | ||
+ | |||
+ | void func(Animal* p) { | ||
+ | if (typeid(*p) == typeid(Dog)) { // 型が Dog なら | ||
+ | // ・・・ | ||
+ | } else if (typeid(*p) == typeid(Cat)) { // 型が Cat なら | ||
+ | // ・・・ | ||
+ | } | ||
+ | } | ||
+ | }} | ||
+ | &font(70%){コンパイル時に型が判明する場合は if constexpr などもアリですが、本稿は実行時に判断する場合の話です。}; | ||
+ | |||
+ | |||
+ | これで十分事足りるのですが、もう少し粋に書きたい場合もあると思います。 | ||
+ | if 文の羅列ではなく switch~case みたいな感じで。 | ||
+ | がしかし switch~case で分岐できる式は整定数式のみです。 | ||
+ | そこで、switch~case とまではいきませんがそれっぽい条件分岐として std::type_index をキーにした std::map を使った書き方です。 | ||
+ | |||
+ | &font(70%){std::type_index は C++11 から使えますが、以下コードは C++17 での書式も使っています。}; | ||
+ | #prettify{{ | ||
+ | int main(void){ | ||
+ | using namespace std; | ||
+ | |||
+ | // クラス定義 | ||
+ | struct Car { | ||
+ | virtual ~Car() {} | ||
+ | }; | ||
+ | struct Nissan : public Car {}; | ||
+ | struct Silvia : public Nissan {}; | ||
+ | struct Skyline : public Nissan {}; | ||
+ | struct Toyota : public Car {}; | ||
+ | struct Chaser : public Toyota {}; | ||
+ | |||
+ | const std::vector<std::shared_ptr<Car>> v = { // サンプル | ||
+ | make_shared<Nissan>(), | ||
+ | make_shared<Silvia>(), | ||
+ | make_shared<Skyline>(), | ||
+ | make_shared<Toyota>(), | ||
+ | make_shared<Chaser>(), | ||
+ | make_shared<Car>(), | ||
+ | }; | ||
+ | |||
+ | // それぞれの型に対応したコードを std::map で構築 | ||
+ | // 注: static 変数であることが実行速度では有利です(この例では1度しか通らないのでアレですが・・) | ||
+ | static const std::map<std::type_index, std::function<void(void)>> map = { | ||
+ | {typeid(Nissan), [] { | ||
+ | cout << "NISSAN" << endl; | ||
+ | }}, | ||
+ | {typeid(Silvia), [] { | ||
+ | cout << "NISSAN SILVIA" << endl; | ||
+ | }}, | ||
+ | {typeid(Skyline), [] { | ||
+ | cout << "NISSAN SKYLINE" << endl; | ||
+ | }}, | ||
+ | {typeid(Toyota), [] { | ||
+ | cout << "TOYOTA" << endl; | ||
+ | }}, | ||
+ | {typeid(Chaser), [] { | ||
+ | cout << "TOYOTA CHASER" << endl; | ||
+ | }}, | ||
+ | }; | ||
+ | |||
+ | for (const auto i : v) { | ||
+ | if (auto j = map.find(typeid(*i)); j != map.end()) { // mapから検索 | ||
+ | j->second(); // 実行 | ||
+ | } else { | ||
+ | cout << "unknown" << endl; // 定義ナシ | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | }} | ||
+ | 実行結果 | ||
+ | NISSAN | ||
+ | NISSAN SILVIA | ||
+ | NISSAN SKYLINE | ||
+ | TOYOTA | ||
+ | TOYOTA CHASER | ||
+ | unknown | ||
+ | |||
+ | |||
+ | |||
+ | この程度のボリュームの例でも、if 文の羅列よりは見通しもよいかなーと思います。 | ||
+ | |||
+ | |||
+ | ちなみに C++03 くらいのコンパイラでも RTTI がサポートされていれば同じこと(型がキーの std::map)が出来るので目新しい小技ではありません。がしかしココまでシンプルには書けません。 | ||
+ | C++は難しげな機能も沢山追加されていますが地味な改修もとても有難いです。 | ||
+ | C++関係者の皆様には感謝です。 |
(This host) = https://thinkridge.com