4

I'm trying to print the key of a certain value in my dictionary. I defined my map like this:

std::map<std::string, std::variant<float,int,bool,std::string>> kwargs; 
kwargs["interface"] = "probe"; 
kwargs["flag"] = true; 
kwargs["Height"] = 5; 
kwargs["length"] = 6; 

I tried to print the value normally but a (no operator "<<" matches these operands) error occurs.

std::cout << kwargs["flag"] << std::endl; 

Can someone help me with this error?

  • FYI: `std::endl` is usually unnecessary. `"\n"` works fine, is just as portable, and is often faster. Use `std::endl` (which is equivalent to `"\n"` followed by `std::flush`) only when necessary. – HTNW Jul 03 '20 at 02:58

3 Answers3

3

There's no operator<< for std::variant, which can't be printed out directly. You need to read the value from the variant (by index or type), e.g.

std::cout << std::get<bool>(kwargs["flag"]) << std::endl; 

Or

std::cout << std::get<2>(kwargs["flag"]) << std::endl; 
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • It works for bool and int type but for string and float I get an error –  Jul 03 '20 at 02:16
  • @Miléna In your code you're not assigning `float`. It should work fine with `std::string`. https://wandbox.org/permlink/al0lzifbjwuf6nqf – songyuanyao Jul 03 '20 at 02:23
  • weird I'm running my code on visual studio 2019 and an error occurs for strings and float –  Jul 03 '20 at 02:55
3

You can define operator<< for your type:

std::ostream& operator<<(std::ostream& os, std::variant<float,int,bool,std::string> const& v) {
    std::visit([&os](auto const& x) { os << x; }, v);
    return os;
}

You can generalize the solution to work with any variant specialization:

template <class Var, class = std::variant_alternative_t<0, Var>>
std::ostream& operator<<(std::ostream& os, Var const& v) {
    std::visit([&os](auto const& x) { os << x; }, v);
    return os;
}

Note, the second template parameter exists to disable the definition if the Var is not a std::variant specialization.

nicolai
  • 1,140
  • 9
  • 17
2

I would use std::visit with a generic lambda that prints any type to std::cout, as follows:

std::map<std::string, std::variant<float,int,bool,std::string>> kwargs; 
kwargs["interface"] = "probe"s;
kwargs["flag"] = true; 
kwargs["Height"] = 5; 
kwargs["length"] = 6; 

for (const auto& [k, v] : kwargs){
    std::cout << k << " : ";
    std::visit([](const auto& x){ std::cout << x; }, v);
    std::cout << '\n';
}

This works because all of float, int, bool, and std::string have overloads for the output stream operator <<. For other types, or to override this behaviour, you could use a custom functor class instead of a lambda:

struct PrintVisitor {
    template<typename T>
    void operator()(const T& t) const {
        std::cout << t;
    }

    // prints std::string in quotes
    void operator(const std::string& s) const {
        std::cout << '\"' << s << '\"';
    }
};

[...]

std::visit(PrintVisitor{}, kwargs);

NOTE: unfortunately, the line kwargs["interface"] = "probe"; doesn't actually select the std::string constructor for the variant type, because the string literal preferentially converts to a bool rather than a std::string (further reading). You can avoid this by explicitly making a std::string using, for example, std::string{"probe"} or the standard std::string user-defined literal, as in "probe"s.

Live demo

alter_igel
  • 6,899
  • 3
  • 21
  • 40
  • 4
    [That last bit has very recently changed, in C++20](https://stackoverflow.com/a/61298916/5684257). It's not implemented yet in some standard libraries, but eventually that line will work as expected. – HTNW Jul 03 '20 at 02:54