I have a map of pointer to member declared as :
std::map<char, T (Operand::*)(const T &, const T &)> op_map;
I fill my map with pointer to member directly in the constructor of my class with :
op_map['+'] = &Operand::op_add;
For example, op_add source code is :
T op_add(const T & a, const T & b) {
return a + b;
}
And I want to call my pointer to member from a const function. Here is the source code :
IOperand *res_int32(char op, const IOperand & rhs) const {
IOperand *res = const_cast<IOperand *>(&rhs);
Operand<int> *tmp = dynamic_cast<Operand<int>*>(res);
T res_calc = (this->*op_map[op])(_value, (T)tmp->getValue());
}
But it makes me always an error :
Operand.hpp:70:64: error: passing ‘const std::map<char, double (Operand<double>::*)(const double&, const double&), std::less<char>, std::allocator<std::pair<const char, double (Operand<double>::*)(const double&, const double&)> > >’ as ‘this’ argument of ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = char, _Tp = double (Operand<double>::*)(const double&, const double&), _Compare = std::less<char>, _Alloc = std::allocator<std::pair<const char, double (Operand<double>::*)(const double&, const double&)> >, std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = double (Operand<double>::*)(const double&, const double&), std::map<_Key, _Tp, _Compare, _Alloc>::key_type = char]’ discards qualifiers [-fpermissive]
Operand.hpp:70:64: error: invalid conversion from ‘const Operand<double>* const’ to ‘Operand<double>*’ [-fpermissive]
Have you got any solution ?
Thank you.
operator[] can't be applied to a const map, since it inserts a new element if the key is not found.
In C++11, there is an at function which throws an exception if the key is not found:
T res_calc = (this->*op_map.at(op))(_value, (T)tmp->getValue());
^^^^^^^
In C++03, you'll need to use find:
map_type::const_iterator found = op_map.find(op);
if (found != op_map.end()) {
T res_calc = (this->*(found->second))(_value, (T)tmp->getValue());
} else {
// handle error
}
You'll also need to change the type of the member functions in the map to
T (Operand::*)(const T &, const T &) const
^^^^^
in order to call them on this from a const member function.
Just make op_add a const member function.
T op_add(const T & a, const T & b) const // <<<
{
return a + b;
}
And instead of the std::map::operator[] use std::map::find http://www.cplusplus.com/reference/stl/map/find/
EDIT:
You also need to change the map type to std::map<char, T (Operand::*)(const T &, const T &) const> op_map, as correctly pointed by R. Martinho Fernandes.
If you know what you are doing, you can try to compile with the c++ flag -fpermissive as G++ said.
Related
I have a bunch of objects in a class hierarchy and would like to make a std::map using references to those objects as the keys in the map. Its seems like std::reference_wrapper would be exactly what is needed for this, but I can't seem to make it work. What I've tried so far:
class Object { // base class of my hierarchy
// most details unimportant
public
virtual bool operator< (const Object &) const; // comparison operator
};
std::map<std::reference_wrapper<const Object>, int> table;
auto it = table.find(object);
table[object] = 42;
table[object]++
However, I always get somewhat obscure errors from the compiler:
/usr/include/c++/4.5.3/bits/stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = std::reference_wrapper<const Object>]’:
/usr/include/c++/4.5.3/bits/stl_tree.h:1522:38: instantiated from ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::find(const _Key&) [with _Key = std::reference_wrapper<const Object>, _Val = std::pair<const std::reference_wrapper<const Object>, int>, _KeyOfValue = std::_Select1st<std::pair<const std::reference_wrapper<const Object>, int> >, _Compare = std::less<std::reference_wrapper<const Object> >, _Alloc = std::allocator<std::pair<const std::reference_wrapper<const Object>, int> >, std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::pair<const std::reference_wrapper<const Object>, int> >]’
/usr/include/c++/4.5.3/bits/stl_map.h:697:29: instantiated from ‘std::map<_Key, _Tp, _Compare, _Alloc>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::find(const key_type&)[with _Key = std::reference_wrapper<const Object>, _Tp = int, _Compare = std::less<std::reference_wrapper<const Object> >, _Alloc = std::allocator<std::pair<const std::reference_wrapper<const Object>, int> >, std::map<_Key, _Tp, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::pair<const std::reference_wrapper<const Object>, int> >, key_type = std::reference_wrapper<const Object>]’
testfile.cpp:39:31: instantiated from here
/include/c++/4.5.3/bits/stl_function.h:230:22: error: no match for ‘operator<’ in ‘__x < __y’
The error seems to be saying it can't compare two std::reference_wrapper<const Object> objects, but it seems like it should be possible -- std::reference_wrapper has a conversion operator that can implicitly convert it to a T& (const Object & here), and Object has a operator <, so why doesn't it work?
Should it work and this is merely a bug in g++? Or is something else going on?
By default std::map uses std::less<std::reference_wrapper<const Object>> as Compare, but std::reference_wrapper<T> doesn't forward operator<() to the underlying type T.
The simplest and concisest option to solve your problem is to define std::less<const Object> (or std::greater<const Object>) in the map definition like this:
std::map<std::reference_wrapper<const Object>, int, std::less<const Object>> table;
It will work correctly and as expected due to implicit conversion of std::reference_wrapper to T& and implicit constructor of std::reference_wrapper.
Example.
It seems that it would work if you made the comparison operator a free function (that perhaps calls a virtual member function).
If it is a member function, a < b really means a.operator<(b); and implicit conversions are not considered for the left-side argument.
On Visual Studio 11 Beta, I get the same problem.
Using the free version which calls the < operator solves the problem.
#include<map>
#include<iostream>
using namespace::std;
class Object {
int _n1;
public:
Object(int n = 0):_n1(n){};
bool operator < (const Object& rhs) const {return this->_n1 < rhs._n1;}
friend ostream &operator << (ostream &stream, const Object& o) { stream << o._n1 << " "; return stream;}
};
struct ObjectLess{
bool operator()(const Object& lhs, const Object& rhs) const
{
return lhs<rhs;
}
};
int main(int argc, char* argv[])
{
//This does not compile
//std::map<std::reference_wrapper<const Object>, string> table;
//Using the free function works
std::map<std::reference_wrapper<const Object>, string, ObjectLess> table;
Object a(1);
Object b(2);
Object c(3);
table[a]="One";
table[c]="Three";
table[b]="Two";
for(auto y: table){
cout << y.first << " " << y.second.c_str() << std::endl;
}
return 0;
}
#include <iostream>
#include <set>
#include <algorithm>
class A
{
private:
int n;
public:
A(int n_):n(n_){}
friend bool operator < (const A& a1, const A& a2) //why must be friend, but cannot be member func
{
return a1.n < a2.n;
}
friend std::ostream& operator<<(std::ostream& os, const A& a)
{
os << a.n;
return os;
}
friend class MyLess;
};
class A definition is listed above
class MyLess
{
public:
bool operator()(const A& a1, const A&a2)
{
return a1.n%10 < a2.n%10;
}
};
int main()
{
std::multiset<A, MyLess> st2;
st2.insert(A(11));
st2.insert(A(2));
st2.insert(A(5));
st2.insert(A(19));
std::cout << "count: " << st2.count(A(2)) << "\n"; //compile error
}
multiset with self-defined comparison rules. when call multiSetObject.count() has compile error? why last line has compile error, if I define st2 using std::multiset<A>, then call st2.count(), then it's ok?
Also, in class A, when I overload operator<. why it must be a friend function, but cannot be member function
Compile error:
[ 50%] Building CXX object CMakeFiles/test13.dir/main.cpp.o
In file included from /usr/include/c++/5/set:60:0,
from /home/roaddb/Desktop/ReviewCPPBook/Video/test13/main.cpp:2:
/usr/include/c++/5/bits/stl_tree.h: In instantiation of ‘std::pair<std::_Rb_tree_const_iterator<_Val>, std::_Rb_tree_const_iterator<_Val> > std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::equal_range(const _Key&) const [with _Key = A; _Val = A; _KeyOfValue = std::_Identity<A>; _Compare = MyLess; _Alloc = std::allocator<A>]’:
/usr/include/c++/5/bits/stl_tree.h:2320:61: required from ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::size_type std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::count(const _Key&) const [with _Key = A; _Val = A; _KeyOfValue = std::_Identity<A>; _Compare = MyLess; _Alloc = std::allocator<A>; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::size_type = long unsigned int]’
/usr/include/c++/5/bits/stl_multiset.h:650:30: required from ‘std::multiset<_Key, _Compare, _Alloc>::size_type std::multiset<_Key, _Compare, _Alloc>::count(const key_type&) const [with _Key = A; _Compare = MyLess; _Alloc = std::allocator<A>; std::multiset<_Key, _Compare, _Alloc>::size_type = long unsigned int; std::multiset<_Key, _Compare, _Alloc>::key_type = A]’
/home/roaddb/Desktop/ReviewCPPBook/Video/test13/main.cpp:78:45: required from here
/usr/include/c++/5/bits/stl_tree.h:1727:4: error: no match for call to ‘(const MyLess) (const A&, const A&)’
if (_M_impl._M_key_compare(_S_key(__x), __k))
^
/home/roaddb/Desktop/ReviewCPPBook/Video/test13/main.cpp:38:10: note: candidate: bool MyLess::operator()(const A&, const A&) <near match>
bool operator()(const A& a1, const A&a2)
^
/home/roaddb/Desktop/ReviewCPPBook/Video/test13/main.cpp:38:10: note: passing ‘const MyLess*’ as ‘this’ argument discards qualifiers
In file included from /usr/include/c++/5/set:60:0,
from /home/roaddb/Desktop/ReviewCPPBook/Video/test13/main.cpp:2:
/usr/include/c++/5/bits/stl_tree.h:1729:9: error: no match for call to ‘(const MyLess) (const A&, const A&)’
else if (_M_impl._M_key_compare(__k, _S_key(__x)))
^
/home/roaddb/Desktop/ReviewCPPBook/Video/test13/main.cpp:38:10: note: candidate: bool MyLess::operator()(const A&, const A&) <near match>
bool operator()(const A& a1, const A&a2)
^
/home/roaddb/Desktop/ReviewCPPBook/Video/test13/main.cpp:38:10: note: passing ‘const MyLess*’ as ‘this’ argument discards qualifiers
The big hint is in
note: passing ‘const MyLess*’ as ‘this’ argument discards qualifiers
multiset is going to provide a const MyLess, so the Function call operator must be const so that it will be compatible with a const this.
Use
bool operator()(const A& a1, const A&a2) const
^ added
instead of
bool operator()(const A& a1, const A&a2)
This question already has an answer here:
std::map access operator deprecated? no operator [] matches these operands
(1 answer)
Closed 8 years ago.
I made a specialization for a bidirectional map when the key type and value type are the same.
Also I made 2 definitions for the operator[] ,one to return constant and one to return non constant. But this did not solve my problem.I get an error that i`m passing constant as *this...
Here it is the specialization:
template<class A>
class BidirectionalMap<A,A>
{
public:
void insert(A a,A b)
{
m1.insert(std::pair<A,A> (a,b));
m1.insert(std::pair<A,A> (b,a));
}
BidirectionalMap& operator =(BidirectionalMap &a)
{
m1=a.m1;
return *this;
}
const A& at(const A& a) const
{
return m1.at(a);
}
int size() const
{
return m1.size();
}
int count(const A& a) const
{
return m1.count(a);
}
A& operator[](const A& a)
{
return m1[a];
}
const A& operator[](const A& a) const
{
return m1[a];////here pinpoints me that error: passing 'const std::map<int, int, std::less<int>, std::allocator<std::pair<const int, int> > >' as 'this' argument of 'std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = int; _Tp = int; _Compare = std::less<int>; _Alloc = std::allocator<std::pair<const int, int> >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = int; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = int]' discards qualifiers [-fpermissive]
}
private:
std::map<A,A> m1;
};
And the error shows in this context:
BidirectionalMap<int, int> f;
f.insert(3, 18);
f.insert(8, 2);
f.insert(7, 5);
f.insert(9, 1);
const BidirectionalMap<int, int> cf = f;
if( f.at(5) == 7 &&
f.count(12) == 0 &&
f.at(8) == 2)
{
yourMark = cf[18] + cf[9];//here is the error
}
Any idea?
operator[] is typically not const in std::map
See http://www.cplusplus.com/reference/map/map/operator%5B%5D/
The reason is that operator[] allows to insert if the element is not present.
I can't find a simpler way to explain my problem than straight pasting it here(a simplified version though).
I have a templated class with necessary assignment operator, default constructor and a common copy constructor. when I try to use this class in my code I get an error as follows:
#include<map>
#include<vector>
template<class T>
class BufferContainer
{
public:
BufferContainer& operator=(BufferContainer& other)
{
buffer = other.get();
return *this;
}
BufferContainer( const BufferContainer& other ) :
buffer( other.get() )
{
}
BufferContainer(){
}
std::vector<T>& get() {
return buffer;
}
void add(T value) {
buffer.push_back(value);
}
std::vector<T> buffer;
};
int main()
{
std::map<int, BufferContainer<int> > myMap;
myMap[1].add(1);
return 1;
}
and the error is:
Practice $ g++ template.cpp
template.cpp: In instantiation of ‘BufferContainer<T>::BufferContainer(const BufferContainer<T>&) [with T = int; BufferContainer<T> = BufferContainer<int>]’:
/usr/include/c++/4.7/bits/stl_pair.h:105:31: required from ‘std::pair<_T1, _T2>::pair(const _T1&, const _T2&) [with _T1 = const int; _T2 = BufferContainer<int>]’
/usr/include/c++/4.7/bits/stl_map.h:458:11: required from ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = int; _Tp = BufferContainer<int>; _Compare = std::less<int>; _Alloc = std::allocator<std::pair<const int, BufferContainer<int> > >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = BufferContainer<int>; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = int]’
template.cpp:38:9: required from here
template.cpp:16:26: error: passing ‘const BufferContainer<int>’ as ‘this’ argument of ‘std::vector<T>& BufferContainer<T>::get() [with T = int]’ discards qualifiers [-fpermissive]
I will appreciate if you help me with the methods to solve this issue and, more importantly, tell me why i got this error.
thanks
Your get() function is not const-qualified, and you are invoking it through a reference to const in your copy-constructor:
BufferContainer( const BufferContainer& other ) :
// ^^^^^
buffer( other.get() )
// ^^^^^^^^^^^
{
}
This is why the compiler is complaining. You cannot call a non-const function through a reference to const. Having a reference to const means you're not going to modify the state of the referenced object, so you can only invoke functions that promise not to modify the object's state. That's what the const qualifier on member functions is for - to make that promise.
Therefore, your member function get() should be qualified as const, and return a reference to a const vector:
std::vector<T> const& get() const
// ^^^^^ ^^^^^
{
return buffer;
}
If you need your non-const function get() because you want to allow clients to modify the internal buffer (advice: consider whether this is really a good idea), then you will have to provide two overloads of get():
A const-qualified one that returns a reference to a const vector (like the one shown above)
A non-const-qualified one that returns a reference to a modifiable vector (like the original get())
What it means is that you should provide a const version of your get() method:
const std::vector<T>& get() const
{
return buffer;
}
since the copy constructor rightly takes a const reference:
BufferContainer( const BufferContainer& other )
This means you can only call const methods on other.
Note that the return type is of the const version of get() is a const reference. This is required for const correctness.
other is taken as const reference and get is a non-const method. You can't call a non-const method from a const reference.
I have a small program I want to execute to test something
#include <map>
#include <iostream>
using namespace std;
struct _pos{
float xi;
float xf;
bool operator<(_pos& other){
return this->xi < other.xi;
}
};
struct _val{
float f;
};
int main()
{
map<_pos,_val> m;
struct _pos k1 = {0,10};
struct _pos k2 = {10,15};
struct _val v1 = {5.5};
struct _val v2 = {12.3};
m.insert(std::pair<_pos,_val>(k1,v1));
m.insert(std::pair<_pos,_val>(k2,v2));
return 0;
}
The problem is that when I try to compile it, I get the following error
$ g++ m2.cpp -o mtest
In file included from /usr/include/c++/4.4/bits/stl_tree.h:64,
from /usr/include/c++/4.4/map:60,
from m2.cpp:1:
/usr/include/c++/4.4/bits/stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = _pos]’:
/usr/include/c++/4.4/bits/stl_tree.h:1170: instantiated from ‘std::pair<typename std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(const _Val&) [with _Key = _pos, _Val = std::pair<const _pos, _val>, _KeyOfValue = std::_Select1st<std::pair<const _pos, _val> >, _Compare = std::less<_pos>, _Alloc = std::allocator<std::pair<const _pos, _val> >]’
/usr/include/c++/4.4/bits/stl_map.h:500: instantiated from ‘std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::insert(const std::pair<const _Key, _Tp>&) [with _Key = _pos, _Tp = _val, _Compare = std::less<_pos>, _Alloc = std::allocator<std::pair<const _pos, _val> >]’
m2.cpp:30: instantiated from here
/usr/include/c++/4.4/bits/stl_function.h:230: error: no match for ‘operator<’ in ‘__x < __y’
m2.cpp:9: note: candidates are: bool _pos::operator<(_pos&)
$
I thought that declaring the operator< on the key would solve the problem, but its still there.
What could be wrong?
Thanks in advance.
The problem is this:
bool operator<(_pos& other)
Should be this:
bool operator<(const _pos& other) const {
// ^^^^ ^^^^^
Without the first const, the right-hand side of the comparison (b in a < b) cannot be const, since without const the function may modify its argument.
Without the second const, the left-hand side of the comparison (a in a < b) cannot be const, since without const the function may modify this.
Internally, the key's of a map are always const.
It should be noted that you should prefer to use nonmember functions. That is, better is a free-function:
bool operator<(const _pos& lhs, const _pos& rhs)
{
return lhs.xi < rhs.xi;
}
In the same namespace as your class. (For our example, just underneath it.)
By the way, in C++ there is no need to prefix the declaration of a struct type variable with struct. This is perfect, and preferred:
_pos k1 = {0,10};
_pos k2 = {10,15};
_val v1 = {5.5};
_val v2 = {12.3};
(Though your type names are admittedly named in an unorthodox manner. :P)
Lastly, you should prefer the make_pair utility function for making pairs:
m.insert(std::make_pair(k1,v1));
m.insert(std::make_pair(k2,v2));
It saves you from having to write out the types for the pair, and is generally easier to read. (Especially when longer type names come along.)
Signature of the less than operator needs to be bool operator<(const _pos& other) const, otherwise map can not use this operator in const functions since this member function is declared as non-const.
I think that your definition of operator< is wrong - the right hand side (argument in this case) should be marked const and it should be a const member function, e.g.
bool operator<(const _pos& other) const{
return this->xi < other.xi;
}