Thursday, October 23, 2008

C++ teaser on templates

A rather long while ago, I published a little teaser on std::set and people seemed to like it quite a bit. So here goes another one based on a problem a friend has found at work today. I hope to reproduce the main idea behind the problem correctly, but my memory is a bit fuzzy.

Can you guess why the following program fails to compile due to an error in the call to equals from within main? Bonus points if you don't build it.
struct data {
int field;
};

template< typename Data >
class base {
public:
virtual ~base(void)
{
}

virtual bool equals(const Data& a,
const Data& b) const
{
return a == b;
}
};

class child : public base< data > {
public:
bool equals(const data& a,
const data& b) const
{
return a.field == b.field;
}
};

int
main(void)
{
data d1, d2;
base< data >* c = new child();
(void)c->equals(d1, d2);
delete c;

return 0;
}
Tip: If you make base::equals a pure abstract method, the code builds fine.

6 comments:

John said...

Hmm... this one seems a bit tricky.

I'm not entirely sure, but I'm guessing that the base class' equals is being called (and that's what is causing the error).

For virtual functions to work, they have to match entirely (name, parameters and return type). So I'm guessing that the base class' equals, because its parameters are template types, isn't matching the child class' equals.

Am I anywhere close?

Julio M. Merino Vidal said...

Well, not quite correct. Note that, if you make the base function pure abstract, the code builds and works, which means that the overriding of the function by the subclass is correct.

It doesn't have to do with how the functions are called ;) because the problem affects the build itself!

Matthias Scheler said...

There are three problems:
1.) The equals method of the child class is not virtual.
2.) Because of that main calls the base class equals method.
3.) The data class doesn't have an == operator.

I figured out 1.) to 2.) myself but need the compiler for 3.).

John said...

The child class' equals shouldn't need to be virtual for it to work.

I still can't figure out what's going on :)

John said...

After doing a bit of research, I've got another guess...

The compiler is checking for errors using the base class' equals because that is what type the pointer is.

Virtual functions are bound at runtime, so the compiler doesn't check against the child class' equals.

calzakk said...

Something to do with ambiguity?
i.e. there's two instances of equals(...), so when instantiating an object of type child the compiler doesn't know which one to use?