Pana
Pana

Reputation: 123

During instantiation of class member: "no operator "[]" matches these operands" when accessing std::map value with map[key]

I have been looking at this for quite some time. I don't understand what is bothering it. The minimum code that is giving me the problem is shown bellow. I comment on the line where the error occurs. Compilation errors follow after the code.

#include <map>
#include <cstdlib>
#include <cstdint>

template<std::size_t D>
class myclassA{
private:  
  std::array<std::string, D> a;
  std::map<std::string, std::size_t> a_type_num;
  std::array<std::size_t, D> a_dims;

  // used to initialize a_type_num
  inline std::map<std::string, std::size_t> return_type_dims() const{
    std::map<std::string, std::size_t> temp;
    for(auto const &s: a)
      temp.emplace(s, 0);
    for(auto const &s: a)     
      temp[s]++;
    return temp;
  };

  // used to initialize a_dims
  inline std::array<std::size_t, D> return_site_dims() const{
    std::array<std::size_t, D> temp;
    for(int i=0; i<D; i++)
      temp[i] = a_type_num[a[i]];  // ERROR!!!
    return temp;
  };

public:  
  // constructor
  myclassA(const std::array<std::string,D> &user) :  a(user)
    , a_type_num(return_type_dims())
    , a_dims(return_site_dims())
  {};
};

int main(int argc, char *argv[]){
  myclassA<4> co({"d","d","p","p"});
  return 0;
}

When compiling I get the error:

src/main.cpp(32): error: no operator "[]" matches these operands
            operand types are: const std::map<std::__cxx11::string, std::size_t, std::less<std::__cxx11::string>, std::allocator<std::pair<const std::__cxx11::string, std::size_t>>> [ const std::__cxx11::string ]
        temp[i] = a_type_num[a[i]];  
                            ^
          detected during:
            instantiation of "std::array<std::size_t={unsigned long}, D> myclassA<D>::return_site_dims() const [with D=4UL]" at line 40
            instantiation of "myclassA<D>::myclassA(const std::array<std::__cxx11::string, D> &) [with D=4UL]" at line 45

On the other hand, something like this compiles fine (I mean the code is going to do nothing, but it compiles):

#include <map>
#include <cstdlib>
#include <cstdint>

int main(int argc, char *argv[]){
  // myclassA<4> co({"d","d","p","p"});

  std::array<std::string, 10> a;
  std::map<std::string, std::size_t> a_type_num;
  std::array<std::size_t, 10> temp;
  for(int i=0; i<10; i++)
    temp[i] = a_type_num[a[i]];  

return 0;

So my question is: why is it complaining that no operator "[]" matches these operands?

Upvotes: 1

Views: 1127

Answers (3)

Remy Lebeau
Remy Lebeau

Reputation: 598134

return_site_dims() is declared as a const method, so its this pointer is const myclassA*, and thus the a_type_num member accessed via that pointer is treated as a const std::map object. However, std::map does not have an operator[] that can be called on a const std::map object, hence the compiler error. A map's operator[] inserts a new entry if the requested key is not found, and it can't insert into a const std::map object.

You can use the map's at() method instead, which has a const overload that does not insert missing keys, it throws std::out_of_range exceptions instead:

inline std::array<std::size_t, D> return_site_dims() const{
  std::array<std::size_t, D> temp;
  for(int i=0; i<D; i++)
    temp[i] = a_type_num.at(a[i]); // <-- OK! exception if a[i] is not found...
  return temp;
};

Alternatively, you can use the map's find() method instead:

inline std::array<std::size_t, D> return_site_dims() const{
  std::array<std::size_t, D> temp;
  for(int i=0; i<D; i++) {
    auto iter = a_type_num.find(a[i]);
    if (iter != a_type_num.end())
      temp[i] = iter->second;
    else
      temp[i] = ...; // <-- some default value of your choosing...
  }
  return temp;
};

Upvotes: 2

Leonid
Leonid

Reputation: 738

The reason that it cannot work is that return_site_dims is declared as const, but operator[] is not a const method (it will modify the map if the element is not found). The only way to access a const std::map is using std::map::find or std::map::at if you want to have an exception instead of manual handling. So your loop could be rewritten:

// used to initialize a_dims
std::array<std::size_t, D> return_site_dims() const {
  std::array<std::size_t, D> temp;
  decltype(a_type_num.end()) iter, iter_end = a_type_num.end();
  for (int i=0; i<D; i++) {
    iter = a_type_num.find(a[i]);
    if (iter == iter_end) {
      // HANDLE THIS SOMEHOW
    }
    temp[i] = *iter;
  }
  return temp;
}

Side comment: You don't need to declare inline functions inline, since functions written in the class declaration are automatically so.

Upvotes: 2

Brian Bi
Brian Bi

Reputation: 119562

The [] operator requires a std::map that is not const, because its behaviour when it does not find the key is to modify the map by inserting the key. In your code, you have the return_site_dims method which is a const method, meaning that it only has const access to all the non-static members of the myClassA class, including the a_type_num map. Therefore, this method is not able to use the [] operator on that map. To fix this, you could make return_site_dims a non-const method.

Upvotes: 3

Related Questions