0

I am building up a CRTP interface and noticed some undefined behavior. So, I built up some sample code to narrow down the problem.

#include <iostream>

template <typename T>
class Base {
public:
    int a() const { return static_cast<T const&>(*this).a_IMPL(); }
    int b() const { return static_cast<T const&>(*this).b_IMPL(); }
    int c() const { return static_cast<T const&>(*this).c_IMPL(); }
};

class A : public Base<A> {
public:
    A(int a, int b, int c) : _a(a), _b(b), _c(c) {}

    int a_IMPL() const { return _a; }
    int b_IMPL() const { return _b; }
    int c_IMPL() const { return _c; }
    
private:
    int _a;
    int _b;
    int _c;
};

template <typename T>
void foo(const T& v) {
    std::cout << "foo()" << std::endl;
    
    std::cout << "a() = " << static_cast<Base<T>>(v).a() << std::endl;
    std::cout << "b() = " << static_cast<Base<T>>(v).b() << std::endl;
    std::cout << "c() = " << static_cast<Base<T>>(v).c() << std::endl;
}

int main() {
    A v(10, 20, 30);
    
    std::cout << "a() = " << v.a() << std::endl;
    std::cout << "b() = " << v.b() << std::endl;
    std::cout << "c() = " << v.c() << std::endl;
    
    foo(v);
    
    return 0;
}

The output of this code is:

a() = 10
b() = 20
c() = 30
foo()
a() = 134217855
b() = 0
c() = -917692416

It appears that there is some problem when casting the child class, which implements the CRTP "interface", to the interface itself. This doesn't make sense to me because the class A plainly inherits from Base so, shouldn't I be able to cast an instance of A into Base?

Thanks!

Patrick Wright
  • 1,401
  • 7
  • 13

2 Answers2

0

You copy and slice when you cast to Base<T>.

Cast to a const Base<T>& instead:

std::cout << "a() = " << static_cast<const Base<T>&>(v).a() << std::endl;
std::cout << "b() = " << static_cast<const Base<T>&>(v).b() << std::endl;
std::cout << "c() = " << static_cast<const Base<T>&>(v).c() << std::endl;
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
0

It turns out I was casting incorrectly to a value rather than a reference

std::cout << "a() = " << static_cast<Base<T>>(v).a() << std::endl;

should become

std::cout << "a() = " << static_cast<const Base<T>&>(v).a() << std::endl;
Patrick Wright
  • 1,401
  • 7
  • 13