c++crtpstack-corruption

c++ CRTP stack corruption


when running the following code on MSVC 2013, x64 Debug config, it will show up a message box with this famous error message when quitting the main() function

"Run-Time Check Failure #2 - Stack around the variable 'tmp' was corrupted.". 

The question I can't answer is: Why?

note that no error message will occur when running on Release config. (why?)

disclaimer: this is just a sample code, meaning that I'm trying to use this same design on other classes (one base and several derived) with much more methods and template arguments and with a much more complex data type than the basic int*.

#include <iostream>

template <class T>
class base {
public:
    base() {
        static_cast<T*>(this)->setData();
    }
    ~base() {
        static_cast<T*>(this)->destroyData();
    }

    void print() {
        static_cast<T*>(this)->print_int();
    }
};

class derived : public base<derived> {
public:

    void setData() {
        x = new int();
    }

    void destroyData() {
        delete x;
        x = nullptr;
    }

    void print_int() {
        std::cout << "x = " << *x << std::endl;
    }

private:

    derived() {}
    derived(const derived& other) {}
    inline derived& operator= (derived copy) {}

    int *x;
};

int main() {
    base<derived> tmp;

    tmp.print();

    return 0;
}

EDIT: @Yakk if I understand correctly, you propose as solution this code:

#include <iostream>

template <class T>
class mix_in : public T
{
public:
    mix_in() { (this)->setData(); }
    ~mix_in() { (this)->destroyData(); }
    void print() { (this)->print_int(); }
};

class foo
{
public:

    void setData()
    {
        x = new int();
    }

    void destroyData()
    {
        delete x;
        x = nullptr;
    }

    void print_int()
    {
        std::cout << "x = " << *x << std::endl;
    }

    foo() {}

private:

    int *x;
};

int main()
{
    mix_in<foo> tmp;

    tmp.print();

    return 0;
}

It works just fine, thank you. I'll probably use this pattern since it seems that there's no solution to use CRTP for what I'm trying to do.

But I still would like to understand why the stack corruption happens. In spite of all the discussion around use or not use CRTP for all things, I'd like to understand very precisely why it happens.

Thank you again.


Solution

  • tmp is a base<derived>, not a derived. The point of CRTP is that the base class "knows" the object's actual type because it's being passed as the template parameter, but if you manually create a base<derived>, then the base class functions would think the object is a derived, but it isn't - it's just a base<derived>. Weird things (undefined behavior) happen as a result. (As noted in the other answer, you are also printing out something without setting it...)

    As to your second question, the checks generated by MSVC to detect those sorts of programmer errors appears to be turned off in Release mode, presumably for performance reasons.