1

I'm doing this:

template<typename T> class var_accessor {
public:
    std::set<std::shared_ptr<T>> varset;
    std::map<std::string,std::shared_ptr<T>> vars_by_name;
    std::map<uint32,std::shared_ptr<T>> vars_by_id;
    std::shared_ptr<T> operator[](const uint32& index) { return vars_by_id[index]; }
    std::shared_ptr<T> operator[](const std::string& index) { return vars_by_name[index]; }

    bool is_in_set(std::shared_ptr<T> what) { auto it = varset.find(what); if (it == varset.end()) return false; return true; }
    bool is_in_set(uint32 what) { auto it = vars_by_id.find(what); if (it == vars_by_id.end()) return false; return true; }
    bool is_in_set(std::string& what) { auto it = vars_by_name.find(what); if (it == vars_by_name.end()) return false; return true; }

    bool place(std::shared_ptr<T> what, const uint32 whatid, const std::string& whatstring) {
        if (is_in_set(what)) return false;
        varset.emplace(what);
        vars_by_name.emplace(whatstring,what);
        vars_by_id.emplace(whatid,what);
        return true;
    }
};

Then...

class whatever {
    std::string name;
    std::function<int32()> exec;
};

And:

class foo {
  public:
    var_accessor<whatever> stuff;
};

This works:

std::shared_ptr<whatever> thing(new whatever);
thing->name = "Anne";
thing->exec = []() { return 1; }

foo person;
person.stuff.emplace(thing, 1, thing->name);

Getting the name crashes it:

std::cout << person.stuff[1]->name;

But if I change the operator[]'s to return references, it works fine.

I don't want to be able to accidentally add new elements without adding to all 3 structures, so that's why I made

std::shared_ptr<T> operator[]

instead of

std::shared_ptr<T>& operator[]

Is there any way to prevent assignment by subscript but keep the subscript operator working?

To be clear I want to be able to keep doing

std::cout << person.stuff[4];

But NOT be able to do

std::shared_ptr<whatever> bob(new whatever);
bob->name = "bob";
person.stuff[2] = bob;

The error is a EXC_BAD_ACCESS inside the std::string class madness Everything I read says simply "don't return references if you want to prevent assignment" but it also prevents using it for me.

Yes I know some things should be made private but I just want to get it working first.

Using Clang/LLVM in XCode 5.1 Thanks!

std''OrgnlDave
  • 3,912
  • 1
  • 25
  • 34

1 Answers1

4

You should return a const reference. See this question

A const reference means the caller is not allowed to change the value, only look at it. So assignment will be a compile-time error. But using it will work (and be efficient).

Community
  • 1
  • 1
Dwayne Towell
  • 8,154
  • 4
  • 36
  • 49
  • Thanks! I don't know why all those google sources said "don't return a reference." Maybe I was asking the question wrong. – std''OrgnlDave Jul 04 '14 at 19:00
  • Returning a non-const reference allows the caller to "mess with" your data, so that is bad, fortunately C++ allows const references which basically give you the best of both worlds. – Dwayne Towell Jul 04 '14 at 19:02