When compiling my code (you will see why this big and not an MRE snippet later)
    template<typename Elem>
    class base_stream
    {
    protected:
        array_list<Elem>* buffer_ = nullptr; // array_list is my class, it works
    public:
        virtual void open() = 0;
        virtual void close() = 0;
    };
    template<typename Elem>
    class base_input_stream : public base_stream<Elem>
    {
    public:
        void open() override
        {
            this->buffer_ = new array_list<Elem>;           
        }
        void close() override
        {
            this->buffer_ = nullptr;
        }
        Elem* read_until(Elem terminator)
        {
            Elem e;
            while((e = read()) != terminator) this->buffer_->add(e);
            return this->buffer_->as_array();
        }
        virtual Elem read();
    };
    class input_stream : public base_input_stream<char>
    {
    private:
        FILE* file_;
    public:
        input_stream(FILE* f)
        {
            file(f);
            open();
        }
        input_stream() { open(); }
        ~input_stream()
        {
            close();
            delete buffer_;
        }
        char* read_line()
        {
            return this->read_until('\n');
            // the problem is here, when I remove read_line, everything works.
        }
        char read() override
        {
            return fgetc(file_);
        }
        void file(FILE* f)
        {
            file_ = f;
        }  
};
and the cpp file
msl::input_stream is(stdin);
msl::stdos.write("Hello, ");
msl::stdos.write(is.read_line());
(please don't judge me for what I'm doing...)
this error appears:
Undefined symbols for architecture x86_64:
  "msl::base_input_stream<char>::read()", referenced from:
      vtable for msl::base_input_stream<char> in msl-test-9638bd.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
but (strangely) this works:
#include <iostream>
template<typename E>
class X
{
};
template<typename E>
class Y : public X<E>
{
public:
    void doStuff(E foo)
    {
        std::cout << foo << std::endl;
    }
};
class Z : public Y<char>
{
public:
    void hmm() {
        doStuff('h');
        // or Y<char>::doStuff('h');
        // or this->doStuff('h');
    }
};
Z hmm = Z();
and compiles without any errors. (the output is as expected h)
What is the problem here. The error is strange because other methods work and only read_line doesn't
