ぷろみん

プログラミング的な内容を扱ってます

C++20時代のポリモーフィズム

概要

ポリモーフィズム良いですよね。
でも、継承嫌ですよね。
MixInみたいな綺麗な継承が使える場面も現実的なサービスを考えると難しい。
そこで簡単に追加、削除ができ可読性も悪くないstd::variantとconceptを使ったC++20時代のポリモーフィズムを紹介します。

コード

#include <iostream>
#include <vector>
#include <string>
#include <variant>

struct A{};
struct B
{
    std::string ToString()
    {
        return "B";
    }
};
struct C{};
struct D{};
struct E{};

using VariantType = std::variant<A, B, C, D, E>;

template<class T>
concept bool Printable = requires(T t){
    t.ToString();
};

void PrintIfPrintable(VariantType variant)
{
    std::visit([](auto&& arg)
        {
            if constexpr (Printable<decltype(arg)>)
            {
                std::cout << arg.ToString() << std::endl;
            }
        }, variant);
}

int main()
{
    std::vector<VariantType> variants;
    variants.emplace_back(A());
    variants.emplace_back(B());
    variants.emplace_back(C());
    variants.emplace_back(D());
    variants.emplace_back(B());
    variants.emplace_back(E());

    for(const auto& variant : variants)
    {
        PrintIfPrintable(variant);
    }
}
// 出力
B
B

visitがやや複雑なのを除けば素直な実装なのではないでしょうか。

variant

variantは状態として特定の型を持つという非常にポリモーフィズムと親和性の高い特徴を持っています。
現在の型にアクセスするためにはstd::visitを使わなければならないという制約は付くもののvariantで定義した型全てに同様の処理を加えることが簡単にできます。
また、継承によるポリモーフィズムにありがちな、このインターフェース継承しているクラスが見つからない問題を解決できることは素晴らしいです。
継承しているクラスは定義ジャンプで到達できないので大規模なプロジェクトだと数十秒かかる検索で探さないといけません。

concept

上記コードはWandboxで動作します。
Compiler optionsに-fconceptsを付けるのを忘れずに。
コンセプトの強さはこの雑に使い方書いたら動くという所にあります。
かなり実務よりな機能なので早く標準に入って欲しいです。