How can I make boost::variant return T() when what() != T, or how do I check if T==what()?

101 views Asked by At

I have this code:

std::vector<boost::variant<int,float,std::string>> data;
template<typename T> T Get(size_t i)
{
    if (i < data.size())
        return boost::get<T>(data[i]);
    return T();
}

how can I check if get<T> failed so I can return T() (without exceptions as it's very costly for performance)?

1

There are 1 answers

1
sehe On BEST ANSWER

In general you can't.

If you know the type indices, you can - perhaps - do something brittle with variant::which.

The sure-fire way would be to write a visitor yourself though. Here's a proof of concept:

namespace detail {
    template <typename T>
    struct get_with_default_visitor : boost::static_visitor<T> {
        T default_;
        get_with_default_visitor(T dv) : default_(dv) {}

        T const& operator()(T const& v) const { return v; }

        template <typename Other> T operator()(Other const&) const {
            return default_;
        }
    };
}

template<typename T, typename Container> T Get(Container const& data, size_t i, T defaultvalue = T())
{
    if (i < data.size())
        return boost::apply_visitor(detail::get_with_default_visitor<T>(defaultvalue), data[i]);
    else
        return defaultvalue;
}

See it Live On Coliru with

int main() {
    std::vector<boost::variant<int, float, std::string> > data { 
        42, 3.14f, std::string("hello world") };

    for (int i = 0; i < 5; ++i) std::cout << Get<int>(data, i, -99) << "\t";
    std::cout << "\n";

    for (int i = 0; i < 5; ++i) std::cout << Get<float>(data, i, -9999e99) << "\t";
    std::cout << "\n";

    for (int i = 0; i < 5; ++i) std::cout << "'" << Get<std::string>(data, i, "#error#") << "'\t";
    std::cout << "\n";
}

Printing

42  -99 -99 -99 -99 
-inf    3.14    -inf    -inf    -inf    
'#error#'   '#error#'   'hello world'   '#error#'   '#error#'