Reputation: 4607
I'm going to extend the existing std::map class and add a new function to it:
template<typename key_type, typename value_type>
class CleanableMap : public Cleanable, public std::map<key_type, value_type>
{
CleanableMap(const CleanableMap& in); //not implemented
CleanableMap& operator=(const CleanableMap& in); //not implemented
public:
CleanableMap() {}
CleanableMap(const std::map<key_type, value_type>& in) { *this = in; }
virtual ~CleanableMap() {}
std::map<key_type, value_type>& operator=(const std::map<key_type, value_type>& in)
{
*((std::map<key_type, value_type>*)this) = in;
return *this;
}
};
I've got a copy constructor and assignment operator such that I can simply assign an existing std::map of the same type to my new map:
CleanableMap<DWORD, DWORD> cm;
std::map<DWORD, DWORD> stdm;
cm = stdm;
The problem is, the compiler is complaining with an error that doesn't make sense -- I've explicitly coded for what it's complaining about:
1>c:\dev\proj\commonfunc.cpp(399) : error C2679: binary '=' : no operator found which takes a right-hand operand of type 'std::map<_Kty,_Ty>' (or there is no acceptable conversion)
1> with
1> [
1> _Kty=DWORD,
1> _Ty=DWORD
1> ]
1> c:\dev\proj\templates.h(245): could be 'CleanableMap<key_type,value_type> &CleanableMap<key_type,value_type>::operator =(const CleanableMap<key_type,value_type> &)'
1> with
1> [
1> key_type=DWORD,
1> value_type=DWORD
1> ]
1> c:\dev\proj\templates.h(250): or 'std::map<_Kty,_Ty> &CleanableMap<key_type,value_type>::operator =(const std::map<_Kty,_Ty> &)'
1> with
1> [
1> _Kty=unsigned long, <--- where did it come up with that?
1> _Ty=std::pair<const DWORD,DWORD>, <--- where did it come up with that?
1> key_type=DWORD,
1> value_type=DWORD
1> ]
1> while trying to match the argument list '(CleanableMap<key_type,value_type>, std::map<_Kty,_Ty>)'
1> with
1> [
1> key_type=DWORD,
1> value_type=DWORD
1> ]
1> and
1> [
1> _Kty=DWORD,
1> _Ty=DWORD
1> ]
There 'could be' it mentions on line 245 doesn't make sense -- there is no assignment operator like that (well, it's private. Removing it completely doesn't change anything).
The 'could be' it mentions on line 250 is the assignment operator that I defined, but it was somehow deduced some other non-matching template types. Where did it get those??
Help!!! :)
Upvotes: 0
Views: 4638
Reputation:
std::map
is not intended to be extended by derivation. If you want to add new functionality, do it with stand-alone functions.
Upvotes: 12
Reputation: 12044
What you are running into is probably an artifact of the exact implementation of std::map in your environment. Since std::map was never intended to be derived from, implementations often have either odd specializations or weird-looking internals that serve to make them much faster, but which lead to lots of strange errors if you try to derive from them.
Alternately, there might be some oddities with your other base class - what does 'Cleanable' look like? If you remove the 'Cleanable' base (as a test) does everything else compile? Might there be some weird internal conflict with their names?
Upvotes: 2
Reputation: 137780
A lot of people are dissing inheritance from STL objects. You can make the process safer by using access protection, though: use map
as a private base. Here's a demonstration.
#include <iostream>
#include <map>
using namespace std;
struct cmap : private map<int, int> {
cmap() : map<int, int>() { };
using map<int, int>::operator[]; // repeat for other methods you'd like
};
int main( int argc, char **argv ) {
cmap my_map;
my_map[1] = 2; // behaves like a map
cerr << my_map[1] << " " << my_map[2] << endl;
// map<int, int> *bad_map = &my_map; this does not compile
return 0;
}
Upvotes: 4
Reputation: 1000
std::map<key_type, value_type>& operator=(const std::map<key_type, value_type>& in)
{
*((std::map<key_type, value_type>*)this) = in;
return *this;
}
This sourcecode tries to convert your CleanableMap to std::map. *this is of the type CleanableMap& but the method returns std::map&.
The correct signature of your = operator should be:
CleanableMap &operator=(const std::map<key_type, value_type> &in);
But if you only want to add a "clean" feature to std::map, why don't you just use a function to do the job?
template<typename K, typename V> void clean(std::map<K, V> &map);
To derive from a class with non-virtual destructor should be avoided, as said by JaredPar.
Use a wrapper or function.
Upvotes: 1
Reputation: 754575
Adding onto Neil's answer.
One concrete reason you should not be deriving from std::map is that it does not have a virtual destructor. This means you simply cannot guarantee any resources you allocate as a part of your map will be freed during the destruction of your implementation via a std::map pointer.
std::map<int,int>* pMap = GetCleanableMap();
...
delete pMap; // does not call your destructor
Upvotes: 16