Search code examples
c++variantvisitor-pattern

How to catch std::variant holding wrong type in compile-time?


I have following piece of code:

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

using namespace std;

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

using element_dict = std::map<string, string>;
using element_type = std::variant<string, element_dict, int>;

void foo(element_type elements)
{
    std::visit(overloaded{
            [](string element) { cout << "string\n"; },
            [](element_dict element) { cout << "dict\n";},
            [](auto /*elements*/) { throw std::runtime_error("wrong type"); }
        }, elements);
}

int main()
{
    element_type str_elems = "string_elem";
    foo(str_elems);
    element_type dict_elems = element_dict{ {"string", "string"} };
    foo(dict_elems);
    element_type wrong_type_elems = 5;
    foo(wrong_type_elems); // throws error

    return 0;
}

stdout :

string
dict
libc++abi.dylib: terminating with uncaught exception of type std::runtime_error: wrong type

I have element_type containing several types. Basically I consider it to contain string and element_dict. Here I have situation when somebody added int type to element_type, but forgot to provide required fixes to foo function. Now I detect this situation in run-time throwing exception. Is there any way to detect it in compile-time?


Solution

  • The easiest way to get that situation to fail at compile-time is to simply not include the overload lambda that catches non-string and non-element_dict typed items; that is, remove

    [](auto /*elements*/) { throw std::runtime_error("wrong type"); }
    

    Then it will fail at compile-time. Basically, by including that case, you are specifically telling the compiler that you want it to succeed at compiling such cases; you are opting-in to behavior you don't want.