Search code examples
c++reference-wrapper

How to assign value to a static map of reference_wrappers?


I have multiple child classes derived of a parent class. I would like to resolve one of the static fields of one of the child classes, based on a parameter that is unique across the children through a static method of the parent.

Since C++ does not provide static initialization, in order to do this, I have to register all the children upon application startup. I do this via registerClass.

My problem:

I am getting compile-time error:

error: no matching function for call to 'std::reference_wrapper<std::vector<vk::DescriptorSet> >::reference_wrapper()'

I cannot assign to a reference_wrapper inside a map, on the other hand, I can assign to a reference_wrapper outside of a map via std::ref().

What am I doing wrong here?

Code:

class Parent {
    virtual ~Parent() = default;

    static auto& getRegistry()
    {
        static std::unordered_map<unsigned int, std::reference_wrapper<std::vector<vk::DescriptorSet>>> registry;
        return registry;
    }

    static std::reference_wrapper<std::vector<vk::DescriptorSet>> resolve_sampler_descriptor_sets_by_id(unsigned int id)
    {
        auto& registry = getRegistry();
        auto it = registry.find(id);

        if (it != registry.end())
        {
            return it->second;
        }

        throw std::runtime_error("Object ID not found in registry.");
    }

    static void registerClass(unsigned int id, std::vector<vk::DescriptorSet>& ds)
    {
        getRegistry()[id] = std::ref(ds);
    }
}

class Child : public Parent
{
    inline static const unsigned int TYPE = 1;
    inline static std::vector<vk::DescriptorSet> sampler_descriptor_sets;
}

Registering classes:

Child::registerClass(Child::TYPE, Child::sampler_descriptor_sets);
AnotherChild::registerClass(AnotherChild::TYPE, AnotherChild::sampler_descriptor_sets);

Solution

  • In registerClass, you can't use operator[] because in case the provided key doesn't exist in the map, a default constructed reference_wrapper should be instantiated. However, this is not possible since it makes no sense to create an object supposed to encapsulate a reference without any reference.

    On the other hand, you could first test the existence of the key with find and then insert the new entry if needed:

    if (getRegistry().find(id) == getRegistry().end())
    {
        getRegistry().insert ({id,std::ref(ds)});
    }
    

    or you can simply as @AhmedAEK suggested use emplace.