I wouldn't use it (in any language), but the claim was that it is impossible. It isn't.
I treat std::variant the same way I treat std::tuple, which is that I use them internally/privately and don't expose them as part of a public API.
If I want to expose a std::variant publicly then I take the effort to emulate Rust, which I think everyone agrees has an incredibly useful and elegant enum type so it looks like this:
int main() {
auto s1 = ConnectionState::Disconnected();
auto s2 = ConnectionState::Connecting(3);
auto s3 = ConnectionState::Connected("192.168.1.5", 8080);
for (const auto& state : {s1, s2, s3}) {
state.visit(
[](Disconnected) {
std::cout << "Disconnected\n";
},
[](int retries) {
std::cout << "Connecting (" << retries << " retries)\n";
},
[](const IpAddress& ip) {
std::cout << "Connected to " << ip.host << ":" << ip.port << "\n";
});
}
}
To implement that I currently do need to write out boilerplate like below, but with C++26 I will be able to use the upcoming reflection feature to automatically implement the bulk of this code by reflecting on the std::variant directly:
class ConnectionState : private std::variant<std::monostate, int, IpAddress> {
public:
static auto Disconnected() { return ConnectionState(std::monostate{}); }
static auto Connecting(int retries) { return ConnectionState(retries); }
static auto Connected(std::string host, uint16_t port) {
return ConnectionState(IpAddress{std::move(host), port});
}
template <typename... Fs>
decltype(auto) visit(Fs&&... fs) const {
auto visitor = Overload{std::forward<Fs>(fs)...};
return std::visit(visitor, *this);
}
private:
using std::variant<std::monostate, int, IpAddress>::variant;
};
using Disconnected = std::monostate;
...