Namaran

Code daily. Without assist.

2026-09-18

number_format.cpp

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

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

using Number = std::variant<int, double>;

std::string describe(const Number& n) {
    return std::visit(overloaded{
        [](double d) { return std::to_string(d); },
    }, n);
}

int main() {
    Number a = 3;
    std::cout << describe(a) << '\n';
}

aint3を保持していて、"3"が出力されるつもりですが、実際には"3.000000"になります。何が問題で、どう直せばよいでしょうか?

Answer

overloadedにはdouble用のoperator()しかなく、int用を書き忘れています。Numberに新しい型を足したらswitch (n.index())を直し忘れてもコンパイラが黙っている、という前回の問題と違い、std::visitは型を網羅しているかを一応チェックします。しかし、そのチェックは「渡した実引数の型と完全に一致するか」ではなく「暗黙変換も含めて呼び出し可能か」なので、intからdoubleへの暗黙変換がある限り、これはコンパイルが通ってしまいます。aが実際に保持しているint3は暗黙に3.0へ変換されてdouble版が呼ばれ、std::to_string(3.0)"3.000000"になります。int用のオーバーロードを足せば直ります:

std::string describe(const Number& n) {
    return std::visit(overloaded{
        [](int i) { return std::to_string(i); },
        [](double d) { return std::to_string(d); },
    }, n);
}