std::set with mutable (non-const) elements

86 Views Asked by At

I want std::set to have mutable elements. But std::set provides only const elements. Is there a way to work the problem around?

Why I need mutable set? Let me explain.

I have a class:

struct Cow
{
    Cow(const std::string &name)
      : name(name)
      , age (0)
        {}

    void Moo()
      { std::cout << name << "is mooing." << std::endl; }

    const std::string name;
    int               age ;
};

I want to store elements of this class in std::set. Elements are identified by their name (it is always const). But I also want mutable access to non-const methods and members.

I can store them in std::map instead. But I don't want to store name separately from the object (the name is part of the object).

I can duplicate name. One name is stored as a key in std::map and another is stored inside the object. But it's memory inefficient, also it might cause data inconsistency when the key has one name and the object has another name.

So what can I do?

1

There are 1 best solutions below

0
anton_rh On

You can use mutable keyword to work around the problem.

#include <iostream>
#include <set>
#include <string>

struct Cow
{
    Cow(const std::string &name)
      : name(name)
      , age (0)
        {}

    void Moo()
      { std::cout << name << " is mooing." << std::endl; }

    const std::string name;
    int               age ;
};

struct Set
{
    struct Holder;
    
    struct Less
    {
        struct is_transparent {};
        bool operator()(const Holder      &left, const Holder      &right) const;
        bool operator()(const Holder      &left, const std::string &right) const;
        bool operator()(const std::string &left, const Holder      &right) const;
    };
    
    std::set<Holder, Less> set;
    
    // Mutable.
    Cow &operator[](const std::string &name);

    void New(const std::string &name);
};

struct Set::Holder
{
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    mutable Cow mut;
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    Holder(const std::string &name)
      : mut(name)
        {}
};

bool Set::Less::operator()(const Holder      &left, const Holder      &right) const { return (left.mut.name < right.mut.name); }
bool Set::Less::operator()(const Holder      &left, const std::string &right) const { return (left.mut.name < right         ); }
bool Set::Less::operator()(const std::string &left, const Holder      &right) const { return (left          < right.mut.name); }

Cow &Set::operator[](const std::string &name)
{
    return set.find(name)->mut;
}

void Set::New(const std::string &name)
{
    set.emplace(name);
}

int main()
{
    Set set;
    set.New("Kewin");
    set.New("Marta");

    set["Kewin"].Moo();
    set["Marta"].age++;

    std::cout << "Marta is " << set["Marta"].age << " years old." << std::endl;
};

The output:

Kewin is mooing.
Marta is 1 years old.