I created the following test code to experiment with the Curiously Recurring Template Pattern, in which I have a Base
class with an Interface()
and a Derived
class with an Implementation
. It's modeled directly after the simplest example from the linked Wikipedia page.
#include <iostream>
template <class T>
class Base {
public:
void Interface() {
std::cout << "Base Interface()" << std::endl;
static_cast<T*>(this)->Implementation();
}
};
class Derived : public Base<Derived> {
public:
Derived(int data = 0) : data_(data) {}
void Implementation() {
std::cout << "Derived Implementation(), data_ = " << data_ << std::endl;
}
private:
int data_;
};
int main() {
Base<Derived> b;
b.Interface();
std::cout << std::endl;
Derived d;
d.Interface();
std::cout << std::endl;
return 0;
}
Once compiled, the program runs smoothly and produces the following output:
Base Interface()
Derived Implementation(), data_ = -1450622976
Base Interface()
Derived Implementation(), data_ = 0
The interesting part is the first test, in which a Base
pointer is being cast to a Derived
pointer, from which the function Implementation()
is being called. Inside this function, a "member variable" data_
is being accessed.
To us this is nonsense, but to a compiler it is simply the value at some offset from this object's memory location. However, in this case the Derived
class takes more space than the base class, and so if the compiler thinks it's accessing a data member from a Derived
object, but actually the object is a Base
object, then the memory we are accessing may not belong to this object, or even to this program.
It seems that this programming practice allows us (the programmer) to very easily do very dangerous things, such as making a seemingly reasonable function call that ends up reading from an uncontrolled memory location. Have I interpreted the mechanics of this example correctly? If so, are there techniques within the CRTP paradigm that I've missed to ensure this problem will not show up?