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