Search code examples
c++lambdastlc++17variant

Visit all std::variants in a list and capture an additional value for every visit


I'm trying to implement a state store, which is basically a map of key-value pairs where the value is a std::variant.

The following class works fine. Note that it not only prints the visited value, but also the keys:

class StateStoreTest
{
    typedef std::variant<float, std::string> StateValue;

private:
    std::map<int, StateValue> States;

public:

    void SetValue(int key, const StateValue& value) { States[key] = value; }

    void VisitAllValues()
    {
        for (auto& it : States)
        {
            auto key = it.first;
            std::visit([key](auto&& value) { std::cout << key << "=" << value << "\n"; }, it.second);
        }
    }
};

I can use it like this and the output is as expected:

StateStoreTest sst;
sst.SetValue(1, 123.4f);
sst.SetValue(2, "Test");
sst.VisitAllValues();

Now I don't want to implement a specific visitor implementation in VisitAllValues() but I want to be able to provide a visitor callable to the VisitAllValues() method. In that visitor callable I want to have access to both the key and also the visited value.

So basically from a caller perspective I'd like to be able to do something like this:

sst.VisitAllValues([](auto key, auto&& value){ std::cout << key << "=" << value << "\n"; });

I guess my VisitAllValues() method would have to look something like this:

void VisitAllValues(??? visitor)
{
    for (auto& it : States)
    {
        auto key = it.first;
        std::visit([key](auto&& value) { visitor(key, value); }, it.second);
    }
}

But I couldn't figure out the details (I tried writing VisitAllValues() as a template, but then I ran into problems on the caller side).

How can I implement my VisitAllValues() method so it accepts a visitor which has access to both the id and the value?

Here is the full working toy example to play with.


Solution

  • It seems to me that as follows should works

    template <typename L>
    void VisitAllValues (L const & visitor)
     {
       for (auto& it : States)
        {
          auto key = it.first;
          std::visit([&](auto&& value) { visitor(key, value); }, it.second);
        }
     }
    

    Off Topic: maybe using std::forward is you use forwarding references

    visitor(key, std::forward<decltype(value)>(value));