Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: Correctly overriding virtual function in template class

Consider following code:

template<typename T>
struct MyTempl
{
    virtual void doStuff(const T &value) = 0;
};

struct MyImpl : MyTempl<int>
{
    void doStuff(const int &value) {}
};

struct MyPtrImpl : MyTempl<int*>
{
    void doStuff(const int* &value) {}
};

MyImpl imp1;
MyPtrImpl imp2;

This will not compile correctly: the compiler tells me that doStuff() in MyPtrImpl is pure virtual, ie. I failed to correctly override it. If I however typedef int* to something like int_ptr, and use that as the template argument for MyPtrImpl, everything works out.

Why is the compiler unable to deduct my intent without the typedef?

like image 522
Ancurio Avatar asked Jan 16 '14 21:01

Ancurio


People also ask

Can a template class have virtual functions?

A class template can indeed contain virtual or pure virtual functions. This was employed by Andrei Alexandresu in "Modern C++ Design" to implement the visitor pattern using templates and type lists.

Can a template function override?

You cannot define a virtual template method. override only works for virtual methods, and you can only override methods with the same signature.

Can you override a virtual function?

Because virtual functions are called only for objects of class types, you cannot declare global or static functions as virtual . The virtual keyword can be used when declaring overriding functions in a derived class, but it is unnecessary; overrides of virtual functions are always virtual.

Can a non templated class have a templated member function?

A non-template class can have template member functions, if required. Notice the syntax. Unlike a member function for a template class, a template member function is just like a free template function but scoped to its containing class.


1 Answers

You didn't specify the correct parameter type of the virtual function and thus you're not overriding it. This results in an abstract class since the virtual function is pure.

When specifying a parameter of a const reference type like

void doStuff(const int & value) {}

it's actually an alternative to write

void doStuff(int const & value) {}

which should be considered the "normal" way to declare a constant reference. When read from right to left this means "a reference to constant int". Applying the same pattern to the pointer type int* results in

void doStuff(int* const & value) {}

which compiles fine. But that one can't be written in the order like above, since that would mean something different: read const int* & from right to left and you get "a reference to a pointer to a constant int".

As you mentioned it, an alternative which works with the order from above can be written if you use typedef to alias the pointer type int*:

typedef int *T;
void doStuff(const T & value) {}

which also compiles fine, since T is now seen as a single literal type not introducing the ambiguities from above. In other words, the only option for how to read const T & is "a reference to a const T, which itself is a pointer to int". It's a bit difficult to understand it intuitively, but what's not possible is to understand this as "a reference to a pointer to a constant int", since this would not have a single T in the description.

I recommend to use the keyword override if you're using a C++11 compiler in order to detect problems like this (there are scenarios in which this problem will not result into a compilation error but into unexpected behavior; for example if the virtual function was not pure):

void doStuff(int* const & value) override {}
//                               ^^^^^^^^
like image 122
leemes Avatar answered Sep 18 '22 15:09

leemes