why std::map<int, float> cant use operator[]: error C2678? - c++

Having:
std::map<const int, float> m_areaCost;
I'm trying to compile the following:
inline float getAreaCost(const int i) const {
return m_areaCost[i];
}
Which leads to the following error:
error C2678: binary '[' : no operator found which takes a left-hand operand of type 'const std::map<_Kty,_Ty>' (or there is no acceptable conversion)
I used to think when we call [elementId] we get element value or default element value so I wonder how can such simple case lead to compilation errors?

Presumably m_areaCost is a member of the object that getAreaCost is a member of. However, getAreaCost is marked as a const member function. This means it can't cause any modification to members. So the m_areaCost member is const in this function.
You cannot call operator[] on a const std::map because its effect is that it inserts a new element if it does not yet exist. Instead use std::map::at:
return m_areaCost.at(i);

Because you state that your function is const, and the [] is not a const operator.
As you said so yourself, the [] !creates or returns a new value.. if it cannot create then you cannot use this operator.. I would use find() and then return the .second value if it is there, something like:
auto it = m_areaCost.find(i);
if (it != m_areaCost.end()) return (*it).second

Because you marked the method to be const.
When you do return m_areaCost[i]; you can actually create the entry in the map if the key does not exist, therefore the operation is not constant so you have a mismatch between your constant function and non-constant operator[] of the map.
If you want the member function to be const then you have to use find to find the entry for the key.

The reason is that getAreaCost being declared as const, like others has stated, but I'd like to give some more suggestions:
A function should always return a useful value, so I would suggest getAreaCost to be:
inline float getAreaCost(int i) const {
std::map<const int, float>::const_iterator It = m_areaCost.find(i);
if (It != m_areaCost.end()) return It->second;
return 0.0f;
}
Some points:
1. input parameter is pass-by-value, so no need forconst int i, just use int i.
2. if you find things like std::map<const int, float>::const_iterator is verbose, give it a typedef. (If you dont, feel free to keep write it, of course :P)
3. 0.0f is just for example, you can return any default value that is suitable in your case.

Related

Error: no matching member function for call to 'push_back'

Why am I getting the error at the last two lines? The goal is to find the object in a set, and modify its content.
using namespace std;
struct mystruct {
int id;
vector<int> y;
mystruct(const int id):id(id) {}
bool operator<(const mystruct& x) const { return id < x.id; }
bool operator==(const mystruct& x) const { return id == x.id; }
};
void test() {
std::set<mystruct> sx;
mystruct x(1);
x.y.push_back(1); x.y.push_back(2);
sx.insert(x);
//
set<mystruct>::iterator i = sx.find(1);
const mystruct* x1 = &(*i);
const mystruct x2 = *x1;
cout << &(i->y) << endl;
cout << &(x1->y) << endl;
cout << x2.id << endl;
x2.y.push_back(3);
i->y.push_back(4);
}
It seems like the iterator returns a constant object, and doesn't let me use push_back() to modify the vector y. How can I overcome this?
Error:
test.cpp:27:8: error: no matching member function for call to 'push_back'
x2.y.push_back(z);
~~~~~^~~~~~~~~
/opt/local/libexec/llvm-6.0/include/c++/v1/vector:688:36: note: candidate function not viable: 'this' argument has type 'const vector<int>', but method is not marked const
_LIBCPP_INLINE_VISIBILITY void push_back(const_reference __x);
^
/opt/local/libexec/llvm-6.0/include/c++/v1/vector:691:36: note: candidate function not viable: 'this' argument has type 'const vector<int>', but method is not marked const
_LIBCPP_INLINE_VISIBILITY void push_back(value_type&& __x);
^
Since x2 is declared with a const qualifier, i.e. const mystruct x2, C++ compiler considers only const-qualified member functions for all invocations on x2 and any of its members. In particular, it is looking for void push_back (const int& val) const member function to invoke. Obviously, there is no such function, because push_back must modify the container, so the compiler produces an error explaining exactly what's going on:
candidate function not viable: 'this' argument has type 'const vector<int>', but method is not marked const
The only way to address this in your code is to remove const qualifier from x2's declaration.
The reason why you cannot modify x2 is that it is declared const, as has been pointed out by #dasblinkenlight. #songyuanyao's comment is right as for accessed to the object referenced by the iterator, but doesn't quite answer the question because it does not say why set iterators only allow const access.
The reason for this is that, as you know, a std::set is an ordered container whose structure is determined by comparing entries to one another using (by default) operator <. This means that there is a container invariant such that if an item a precedes another item b in the std::set, it follows that !(b < a). I put it like this because this also holds for std::multiset. Since, in a set, duplicates are not allowed, it follows that, actually, if a precedes b, then a < b. If this invariant were to be violated, then any set operation that requires the set to be sorted, such as find or insert, will have unexpected (to be precise, undefined) behavior.
Therefore, a std::set will not allow you to change an item using an iterator, because you could unwittingly change members of the item in a way that affect its proper place in the set, breaking the invariant and thereby causing undefined behavior.
Unfortunately, the compiler is not smart enough to understand that your comparison function only evaluates certain members, in this case, only id. If compiler and language were capable of analyzing and expressing this, they might figure that while i->id should be a reference to const, i->m for any other member m could safely be a reference to non-const.
There are at least four possible solutions to your problem:
The easiest but ugliest solution is to mark the members that you need to modify as mutable. Note that you must yourself ensure that changing them does not affect the sort order.
Another fairly ugly solution is the deliberate use of const_cast as in const_cast<mystruct &>(*i) where i is an iterator. Again, never change a member that has an effect on the sort order in this way.
A more elegant solution that, however, has additional run time overhead, is to add a level of pointer indirection (e.g. using std::unique_ptr) to properties that you want to modify. Note, however, that if you were to use the pointee in your comparison function, you would still run the risk of breaking the set invariant, only now the compiler and library will no longer prevent you from doing so!
The only way that works even if you want to change members affecting the sort order is to make a copy of the item, modify the copy, erase the old item, and then re-insert the copy. This allows the container to insert the copy at a different position if necessary.
Final notes:
A hashed container such as std::unordered_set will have exactly the same issue, only in this case it is not the comparison function, but both the hash and equality functions that you have to consider.
For the given reasons, a std::map or std::unordered_map may be a better match for your problem domain, as, in this case, the library knows that the mapped_type is not used to determine container structure. Note also how the value_type of a std::map or std::unordered_map is std::pair<const key_type, mapped_type> instead of std::pair<key_type, mapped_type> exactly for the same reason that changing the key could break the container invariants.

Defining hash and equality functions for nonstandard objects

I'm having some trouble defining hash and equality functions for objects. I'm using the objects as keys for std::unordered_map's. I have two keys aKey and unaccessibleKey. I can add an equailty operator overload to aKey, but I can't to unaccessibleKey because it's "unaccessible". I have tried doing the following, but I'm not sure if I'm using the right syntax for everything, and I don't know how to define an equality function for unaccessibleKey. Here's what I've tried:
struct aKeyHash
{
std::size_t operator()(const aKey& k) const
{
return k.getnonconstmem()->nonconstfunc();
};
}
struct unaccessibleKeyHash
{
std::size_t operator()(const unaccessibleKey& k) const
{
return k.noncostmem;
};
}
bool UnaccessibleEqualityFunction(const unaccessibleKey& p1, const unaccessibleKey& p2)
{???} //i dont know how to define this
std::unordered_map<aKey, std::unordered_map<unaccessibleKey, aValue, unaccessibleKeyHash, unaccessibleEqualityFunctions>, aKeyHash>
Am I doing this right (aside from the function that I don't know how to define)? As a side note, when I tried calling k.getnonconstmem()->nonconstfunction() I get an error.
It would be possible to use unaccessibleKey::nonconstmem as the key itself because it's actually a hashed int, but that may lead to complications later down the line that I don't want to deal with.
So my questions the are: 1. do I have the right syntax for the hashes, 2. how do I define the equality function, 3. why would I get the error with the const/nonconst mixing?
do I have the right syntax for the hashes
The hash structures themselves have correct syntax. Whether the definitions of the hash functions are correct, depends on the definition of the target types. In particular, if getnonconstmem() is a non-const function, then calling it on a const reference is ill-formed. You may only call const functions on const objects.
how do I define the equality function
Return true when the objects have equal logical state, and false otherwise. An example, which assumes that the state consists of a single member:
return p1.member == p2.member;
why would I get the error with the const/nonconst mixing?
The shown code is not sufficient to explain why there would be errors. You will get errors if you try to modify, or call non-const functions on const objects, or any object through pointer or reference to const.

C++11 searching a vector of objects

I have 2 vectors
std::vector<MyObj> v;
std::vector<MyObj2> z;
The objects in the vectors both contain an int that has an ID. I want to see if when looking through v it has a matching id in z
So I thought that I could use `std::find_if and a Lambda.
for (int i=0; i < _z.size(); i++)
{
MyObj2 _g = _z.at(i);
auto iter = std::find_if(v.begin(), v.end(), [this](MyObj o)
{
if (o.getID() == _g.getID())
{
std::cout << "we have a match" << std::endl;
}
else
{
std::cout << "we DO NOT have a match" << std::endl;
}
});
}
but I am getting an error that I dont understand.
43: Member function 'getID' not viable: 'this' argument has type 'const MyObj2', but function is not marked const
I dont understand what has to be marked const and why?
an I needing something like in my .hpp?:
MyObj2& operator= (const MyObj2&);
MyObj2& operator== (const MyObj2&);
From cppreference about find_if:
UnaryPredicate must meet the requirements of Predicate.
wich liks to the concept of Predicate:
The Predicate concept describes a function object that takes a single iterator argument that is dereferenced and used to return a value testable as a bool.
In other words, if an algorithm takes a Predicate pred and an iterator first, it should be able to test the iterator using the predicate via a construct like if (pred(*first)) {...}.
The function object pred shall not apply any non-constant function through the dereferenced iterator. This function object may be a pointer to function or an object of a type with an appropriate function call operator.
There are two requirements stated in that text:
Your predicate (i.e. the lambda) has to return something convertible to bool. Some output to cout is not sufficient.
Your predicate is not allowed to call nonconst functions on the argument (i.e. the MyObj)
However, your code shows lots of compile errors but nothing that relates to the error you stated in your question. This is because you did not provide an SSCCE:
You did not capture _g in the lambda expression
You get compile errors if the predicate does not return something convertible to bool (see requirements for the predicate above)
you mismatched z and _z
I do not get the errors you described, because in your example code you copy the values from the vectors instead of taking a const reference. Copying is fine of course, and you can apply any non-const functions to those copies. I have a compileable mini-example of your code here: http://ideone.com/o8tPED
However, this is not how it should be implemented:
You should take references instead of copies of the vector elements (I am almost sure you do that in reality)
To avoid the error you reported, you will then have to declare both getID functions const. You should do that regardless of the usage in the algorithm, because functions that don't change the object should always say so.
You need to declare MyObj2::getID() as const: The compiler says so quite clearly:
struct MyObj2 {
int getID() const { ... }
...
};
It seems, your got a member _g for which this is captured. The _g mentioned before in this example is not captured because the capture clause explicitly restricts the capture to this. If you used the _g from the scope, getID() wouldn't need to be const.

Weird behavior with map.find and a pointer to a vector

I have a map of pairs to a vector of vectors that looks like this:
std::map<std::pair<uint16, uint16>, std::vector<std::vector<uint32> > >
The map is populated in the constructor of a class. This class provides a public method that returns a pointer to std::vector<std::vector<uint32> > (the map value part), with something like this:
typedef std::pair<uint16, uint16> key;
typedef std::vector<std::vector<uint32> > value;
value* FindValues(key someKey) {
std::map<key, value>::const_iterator it;
it = someStore.find(someKey);
if (it != someStore.end())
return &(value)it->second;
return NULL;
}
This is when it gets weird. When iterating over the vector returned by FindValues, all child vectors have a large, negative number (such as -1818161232) as their first value. But if I use a function like:
value FindValues(key someKey) {
std::map<key, value>::const_iterator it;
return someStore.find(someKey)->second;
}
...then the value is normal. This only happens for the value at index 0 of all child vectors. With the second method, though, my application segfaults if a key wasn't found (for obvious reasons). What am I doing wrong?
If the return statement truly looks as
return &(value) it->second;
then there are several things that can be said about it:
Your compiler is broken if it accepts it without issuing diagnostic messages. In C++ it is illegal to apply built-in unary & to a result of non-reference cast. The (value) it->second expression produces a temporary object, an rvalue. You can't obtain the address of such object by using &. The code should not even compile.
If your compiler accepts it as some kind of weird "extension", then it means that you are indeed obtaining and returning the address of a temporary object. The temporary object is then immediately destroyed, leaving your pointer pointing to garbage. No wonder you see some weird values through such pointer.
The need for some sort of cast arises from the fact that you used const_iterator to store the result of the search. Apparently you made a misguided attempt to cast away constness of it->second with your (value) cast. The correct way to do it might look as follows
return const_cast<value *>(&it->second);
But why did you use const_iterator in the first place? The right thing to do would be to use a regular iterator and just do
return &it->second;
without any extra casts.
You need to decide what kind of FindValue method you are trying to write. If this is supposed to be a constant method, it should return const value * and should be declared as const
const value* FindValues(key someKey) const
and, of course, you should use const_iterator inside in this case.
If your FindValue is supposed to be a non-constant method, then you can keep the current declaration
value* FindValues(key someKey)
but use ordinary iterator inside.
What you have now is some sort of hybrid of the two, which is what makes you to resort to weird casts. (In fact, you will probably need both versions in your class. One can be implemented through the other.)
Your typedefs are quite misleading. This is the erroneous line:
return &(value)it->second;
What appears to be a simple C-style type cast is actually a call to std::vector's copy constructor. This line could be rewritten as
return &std::vector<std::vector<uint32> >(it->second)
The reason for the weird results become visible when you rewrite this line as following:
std::vector<std::vector<uint32> > result (it->second);
return &result;
You are actually returning the address of a local object that will be destroyed as soon as the function returns.
So, this variant will be better.
typedef std::pair<uint16, uint16> key;
typedef std::vector<std::vector<uint32> > value;
value* FindValues(key someKey) {
std::map<key, value>::const_iterator it;
it = someStore.find(someKey);
if (it != someStore.end())
return &const_cast<value&>(it->second);
return 0;
}

How can I check values in a C++ map without getting compiler errors in a "const" member function?

I have a member function that compares data in a C++ map. I declared the member function as constant:
bool operator == (UnitSet otherSet) const;
but as soon as I use myMap["l"] to access and compare values I get compiler errors about myMap being constant. Is there a way around this while keeping the const directive?
I know an empty value is created for the "l" key if it didn't exist when I check it, but that won't change the value / behaviour of my UnitSet - maybe I'm trying to be too picky here though and should just drop the "const". What is the good way to go about this?
Thanks.
Firstly, you should change your function signature to accept otherSet by const reference, as you have no need to copy it and won't be modifying it:
bool operator==(const UnitSet& otherSet) const;
Then, rather than using the non-const operator[], use find() or begin()/!=end()/++ to get const_iterators into your maps for the comparison. As find(), begin() and operations on const_iterator only require const access to map, they can be used from your const member function.
map<X, Y>::const_iterator i = the_map.find(key);
if (i != the_map.end())
// here, i->first is the key, i->second is the value
else
// key wasn't in the map after all...
std::map unfortunately doesn't have a const overload of the [] operator. This is because whenever you access a key that doesn't exist, the map creates an element for it; if the map was const, that would be impossible. Therefore, there's no way to call it with a constant map. It isn't related to the behavior of your UnitSet class.
You'll have to use myMap.find(TKey) to get an iterator to your element, which returns myMap.end() if it can't find it.
Map iterators point to a std::pair of your key and value, so you need to access the second member of it to get your value (first being the key).
const UnitSet& reference = myMap.find("l")->second;