c++segmentation-faultstdvectorcrtp

Segmentation Fault while calling push_back on std::vector


Below I have provided a minimal working example of the error I am receiving. At a high level, the application segmentation faults at the line where I attempt to push_back a Point object into the vector defined in DerivedEngine. I have included the derived/CRTP class structure, in case it is material.

GDB provides the following output:

Program received signal SIGSEGV, Segmentation fault.
0x000055555555691d in __gnu_cxx::new_allocator<Point*>::construct<Point*, Point*> (this=0x7fffffffdde8, __p=0x0)
    at /usr/include/c++/9/ext/new_allocator.h:146
146     { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
(gdb) list
141 #if __cplusplus >= 201103L
142       template<typename _Up, typename... _Args>
143     void
144     construct(_Up* __p, _Args&&... __args)
145     noexcept(std::is_nothrow_constructible<_Up, _Args...>::value)
146     { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
147 
148       template<typename _Up>
149     void
150     destroy(_Up* __p)

The working code example is below:

#include <vector>
#include <unordered_map>
#include <iostream>

struct Data
{
     double x_ = 3.0;
     double y_ = 3.14;
};

template<typename Derived>
class AbstractEngine
{
public:
    AbstractEngine(int i)
    {
        add_data(i);
        auto data = view_data(i);
        std::cout << "Added data: " << data->x_ << " " << data << std::endl;
        on_init();
    }

    void on_init()
    {
      static_cast<Derived*>(this)->on_init();
    }

    void add_data(int i)
    {
        data_[i] = new Data(); 
    }

    Data* view_data(int i)
    {
        return data_[i];
    }

private:
    std::unordered_map<int, Data*> data_;
};

struct Point
{
    Point(double x) : x_(x) {} 
    double x_;
};

class DerivedEngine : public AbstractEngine<DerivedEngine>
{
public:
    DerivedEngine(int i) :
        AbstractEngine<DerivedEngine>(i) {}

    void on_init()
    {
        int i = 3;
        auto data = view_data(i);
        std::cout << "Viewed data: " << data->x_ << " " << data << std::endl;
    
        points_.push_back(new Point(3.14));
    }
private:
    std::vector<Point*> points_;
};


int main()
{
    DerivedEngine engine(3);
}

I have attempted a reformulation using unique_ptr instead of a raw pointer, but to no avail. I suspect the issue is related to the vector itself, not the object being pushed into it.


Solution

  • Your constructor of the base-class is calling the method on_init

    AbstractEngine(int i)
    {
        add_data(i);
        auto data = view_data(i);
        std::cout << "Added data: " << data->x_ << " " << data << std::endl;
        on_init();
    }
    

    and in that method you care casting to the derived-class

    void on_init()
    {
      static_cast<Derived*>(this)->on_init();
    }
    

    you are still in the constructor of the base so the lifetime of the derived class has not started yet.

    void on_init()
    {
        int i = 3;
        auto data = view_data(i);
        std::cout << "Viewed data: " << data->x_ << " " << data << std::endl;
    
        points_.push_back(new Point(3.14));
    }
    

    And here you are trying to call push_back on a member that does not yet exist.