0

Below is a simplified version of something I am trying to do.

I am getting a C2678 error on the return line of getMove2(). The only difference between getMove() and getMove2() is const.

I have a work-around (commented-out line), but I'm trying to understand why that line doesn't work, and how else to fix it.

#include <string>
#include <map>

class Dummy
{
public:
    Dummy(std::string character, int a, int b, int c) {
        name = character;

        moves["a"] = a;
        moves["b"] = b;
        moves["c"] = c;
    }

    int getMove(std::string move_name) {
        return moves[move_name];
    }

    int getMove2(std::string move_name) const {
        return moves[move_name];
        //return moves.find(move_name)->second; //This works but I can't figure out how to make the above work
    }
private:
    std::string name;
    std::map <std::string, int> moves;
};

I have tried playing around with having different pieces of the map as const, but no luck.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Eric Saper
  • 11
  • 2
  • 2
    the error code is close to meaningless. You should read teh compiler error message. It should tell you what is wrong. If you do not understand it, no problem, others can explain it if you include it in the question – 463035818_is_not_an_ai Mar 22 '23 at 21:39
  • 1
    in the constructor you use `moves["a"] = a;` where `[]` modifies the map. `operator[]` is non-const. – 463035818_is_not_an_ai Mar 22 '23 at 21:40
  • `moves[move_name];` potentionally changes your map member, and that's not possible for a `const` class instance of `Dummy`. – πάντα ῥεῖ Mar 22 '23 at 21:41
  • 1
    using `find` is perfectly fine to find an element in a map, though you need to handle the case when the element cannot be found – 463035818_is_not_an_ai Mar 22 '23 at 21:41
  • 1
    https://en.cppreference.com/w/cpp/container/map/operator_at – 463035818_is_not_an_ai Mar 22 '23 at 21:42
  • Additional suggestion: use an initializer list in your constructor. E.g. `Dummy(std::string character, int a, int b, int c) : name(character), moves{{"a", a}, {"b", b}, {"c", c}} { }` – Chris Mar 22 '23 at 21:50
  • `moves[move_name]` adds `move_name` to the map if it's not present. Hence the method is not `const`. It's one of those "wtf" things about the C++ standard library. Probably seemed like a good idea at the time. C++20 has a decent fix. Rather unbelievable we had to wait that long. Have a sympathetic upvote! – Bathsheba Mar 22 '23 at 22:17

1 Answers1

2

The class template std::map declares the following methods to access individual elements by key:

T& operator[](key_type&& x);
T& operator[](key_type&& x);
T& at(const key_type& x);
const T& at(const key_type& x) const;

As you can see, both subscript operators are not declared as const methods, so you may not call them on a const std::map object. Since getMove2() is declared as const, its this pointer has the type Dummy const *, and so the moves member is const when accessed through that pointer.

However, you can call the map::at() method which is declared as const.

For example:

int getMove2(std::string move_name) const {
    return moves.at( move_name );
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 1
    Just note that `map::at()` will throw an exception if the specified key does not exist. If you want to return a default value in that case, use `map::find()` instead and check its result, eg: `int getMove2(std::string move_name) const { auto iter = moves.find(move_name); return (iter != moves.end()) ? iter->second : 0; }` – Remy Lebeau Mar 22 '23 at 22:01