Namaran

Code daily. Without assist.

2026-08-23

shape_label.cpp

#include <print>
#include <variant>

struct Circle { double radius; };
struct Square { double side; };

template <class... Fs>
struct overloaded : Fs... { using Fs::operator()...; };
template <class... Fs>
overloaded(Fs...) -> overloaded<Fs...>;

int main() {
    std::variant<Circle, Square> shapes[] = { Circle{2.0}, Square{3.0} };
    for (const auto& shape : shapes) {
        std::string label = std::visit(overloaded{
            [](const Circle& c) { return std::format("circle r={}", c.radius); },
            [](const Square& s) { return std::format("square s={}", s.side); },
        }, shape);
        std::println("{}", label);
    }
}

これは何を出力するでしょうか?switchではなくstd::visitoverloadedを使っている点に注目してください。

Answer

circle r=2square s=3を1行ずつ。shapesCircleSquareのどちらかを保持するstd::variantで、std::visitは今そこに入っている型に応じて、渡した呼び出し可能オブジェクトの中から対応するoperator()を選んで呼び出します。overloadedは複数のラムダを1つの型にまとめるためだけのヘルパで、C++には値の「型」で分岐を書く専用の構文(パターンマッチング)が無いため、std::visit+overloadedが事実上の代用になっています。switchintや列挙型のような整数値でしか分岐できず、std::variantが今どの型を保持しているかを直接switchすることはできません。std::formatは書式文字列と実引数からstd::stringを組み立て、浮動小数点数はここでの2.03.0のように、そのまま表せる値なら末尾の.0を省いた最短表現(23)で出力されます。