tags:

views:

911

answers:

9

I just don't get it. Tried on VC++ 2008 and G++ 4.3.2

#include <map>


class A : public std::multimap<int, bool>
{
public:
 size_type erase(int k, bool v)
 {
  return erase(k); // <- this fails; had to change to __super::erase(k)
 }
};

int main()
{
 A a;
 a.erase(0, false);
 a.erase(0); // <- fails. can't find base class' function?!

 return 0;
}
+25  A: 

When you declare a function in a class with the same name but different signature from a superclass, then the name resolution rules state that the compiler should stop looking for the function you are trying to call once it finds the first match. After finding the function by name, then it applies the overload resolution rules.

So what is happening is the compiler finds your implementation of erase(int, bool) when you call erase(0), and then decides that the arguments don't match.

Greg Hewgill
Good explanation.What's interesting: the same code does work in C#.
Filip
That's because C# is a different language with different overload rules. :)But yeah, I'd agree C#'s overload rules are a lot more intuitive. This problem has tripped me up before as well.
jalf
+6  A: 

First of all, you should never derive from STL containers, because no STL containers define a virtual destructor.
Second of all, see Greg's answer about inheritance.

Marcin
why shouldn't you?
tloach
Added the reason.
Marcin
The deal with virtual destructors is much over-rated in my opinion. If you never use its base type it is never a problem.
Zan Lynx
If you never use its base type, then what's the point? (If you're deriving instead of using it as a member, just to get access to the protected members, then you're doing it wrong.)
Marcin
+9  A: 

You've hidden the base class's erase member function by defining a function in the derived class with the same name but different arguments.

http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.9

J Francis
+14  A: 

1: You need to be extremely careful when deriving from C++ standard library containers. It can be done, but because they don't have virtual destructors and other such niceties, it is usually the wrong approach.

2: Overload rules are a bit quirky here. The compiler first looks in the derived class, and if it finds any overload with the same name, it stops looking there. It only looks in the base class if no overloads were found in the derived class.

A simple solution to that is to introduce the functions you need from the base class into the derived class' namespace:

class A : public std::multimap<int, bool>
{
public:
        using std::multimap<int, bool>::erase; // Any erase function found in the base class should be injected into the derived class namespace as well
        size_type erase(int k, bool v)
        {
                return erase(k);
        }
};

Alternatively, of course, you could simply write a small helper function in the derived class redirecting to the base class function

jalf
A: 

To replace __super in a portable way, define a typedef at the top of your class like this:

typedef std::multimap<int, bool> parent;
public:
    size_type erase(int k, bool v)
    {
            return parent::erase(k);
    }

It does not need to be "parent" of course. It could be any name you like, as long as it is used consistently throughout your project.

Zan Lynx
“I used _Parent because I've seen it that way in some library code.” – Yes! And only library code may/should use names starting with an underscore! So: don't do this in normal code!
Konrad Rudolph
More specifically, only the *standard* library may use names starting with underscores, not just any library. (Specifically, underscore in global namespace, double underscore or underscore followed by capital letter. All of these are *illegal* in your code.)
jalf
That may have made sense in C without effective name hiding but in a private, class local definition, who cares? I'll put underscores where I like them, thanks.
Zan Lynx
No, it makes sense because C++ still has macros which *blindly* replace tokens regardless of namespaces or private scope.The standard library is permitted to define a _Parent macro, which would screw up your code.Just saying your code is not, as you claimed, portable.
jalf
I don't see a need for a typedef. If you're inheriting from one class it shouldn't be too hard to remember which class that was inside the child.
Max Lybbert
template<typename X>class tracking_pool_allocator: public boost::fast_pool_allocator< X, boost::default_user_allocator_new_delete, boost::details::pool::null_mutex, 512 >You *will* want a typedef.
Zan Lynx
If you're writing a templated class, but class A from the original example is not templated.
Max Lybbert
Yes it was. std::multimap is always templated. You're just quibbling now.
Zan Lynx
+4  A: 

Think whether you really want to inherit from std::map. In all the time I've written code, and that's longer than STL exists, I've never seen an instance where inheriting from a std::container was the best solution.

Specifically, ask yourself whether your class IS a multimap or HAS a multimap.

Enno
+2  A: 

Others have answered how to resolve the syntax problem and why it can be dangerous to derive from standard classes, but it's also worth pointing out:

Prefer composition to inheritance.

I doubt you mean for 'A' to explicitly have the "is-a" relationship to multimap< int, bool >. C++ Coding Standards by Sutter/Alexandrescu has entire chapter on this (#34), and Google points to many good references on the subject.

It appears there is a SO thread on the topic as well.

luke
+1  A: 

For those that use Effective C++ as a C++ programming reference, this issue is covered in Item 33 (Avoid hiding inherited names.) in the book.

bradtgmurray
+1  A: 

I agree with others' comments that you need to be very careful inheriting from STL classes, and it should almost always be avoided.

However, this problem could arise with some other base class from which it's perfectly sensible to inherit.

My question is: why not give your 2-argument function a different name? If it takes different arguments, presumably it has a slightly different meaning? E.g. erase_if_true or erase_and_delete or whatever the bool means.

Andy Balaam
It depends which way you look at this problem. Since the function in question only relies on public members of A's base class, one can argue that there is no need for A. Instead, we should follow S. Meyer's advice and use non-member non-friend functions. That's exactly what C# extension methods are.
Filip
Also a good idea.
Andy Balaam