0

I'm relatively new to C++, and this is causing me a lot of headaches. I'm working on a student project about data structures, templates and inheritance. Let's talk about my problem.

I've got this file, container.hpp, that includes its .cpp on the bottom (cause this is supposed to be a template lib), containing all the base classes: Container--->LinearContainer, and Container--->TestableContainer--->SearchableContainer. Let's define this file:

container.hpp


#ifndef CONTAINER_HPP
#define CONTAINER_HPP

#include <functional>

namespace lasd {

/* ************************************************************************** */

class Container {

private:

protected:

    unsigned long size = 0;

public:

    // Destructor
    virtual ~Container() = default;

    // Copy assignment
    Container& operator=(const Container&) = delete;

    // Move assignment
    Container& operator=(Container&&) noexcept = delete;


    // Comparison operators
    bool operator==(const Container&) const noexcept = delete;
    bool operator!=(const Container&) const noexcept = delete;

    // Specific member functions...

};

template <typename Data>
class LinearContainer : virtual public Container {

private:

protected:

public:

    // Destructor
    ~LinearContainer() = default;

    // Copy assignment
    LinearContainer& operator=(const LinearContainer&) = delete;

    // Move assignment
    LinearContainer& operator=(const LinearContainer&&) noexcept = delete;

    // Comparison operators
    bool operator==(const LinearContainer&) const noexcept = delete;
    bool operator!=(const LinearContainer&) const noexcept = delete;

  // Specific member functions..

};

template <typename Data>
class TestableContainer : virtual public Container {

private:

protected:

public:

    // Destructor
    virtual ~TestableContainer() = default;


    // Copy assignment
    TestableContainer& operator=(const TestableContainer&) = delete;

    // Move assignment
    TestableContainer& operator=(TestableContainer&&) = delete;


    // Comparison operators
    bool operator==(const TestableContainer&) const noexcept = delete;
    bool operator!=(const TestableContainer&) const noexcept = delete;

    // Specific member functions...

};

template <typename Data>
class SearchableContainer : virtual public TestableContainer<Data> {

private:

protected:

public:

    // Destructor
    ~SearchableContainer();

    // Copy assignment
    SearchableContainer& operator=(const SearchableContainer&) = delete;

    // Move assignment
    SearchableContainer& operator=(SearchableContainer&&) noexcept = delete;


    // Comparison operators
    bool operator==(const SearchableContainer&) const noexcept = delete;
    bool operator!=(const SearchableContainer&) const noexcept = delete;

    // Specific member functions...



};

/* ************************************************************************** */

#include "container.cpp"

}

#endif

I'm not going to post here the container.cpp cause we need more space for explain the main problem, and container.cpp contains two function implementations that are not related to our issue.

Now, it's time to talk about the effective child class: the vector, derived from LinearContainer and SearchableContainer.

vector.hpp


#ifndef VECTOR_HPP
#define VECTOR_HPP

/* ************************************************************************** */

#include "../container/container.hpp"

/* ************************************************************************** */

namespace lasd {

/* ************************************************************************** */

template <typename Data>
class Vector: public LinearContainer<Data>, public SearchableContainer<Data> {

private:

protected:

    using LinearContainer<Data>::size;
    Data* Elements = nullptr;

public:

    // Default constructor
    Vector() = default;

    // Specific constructor
    Vector(const unsigned long);

    // Copy constructor
    Vector(const Vector&);

    // Move constructor
    Vector(Vector&&) noexcept;

    // Destructor
    ~Vector();

    // Copy assignment
    Vector& operator=(const Vector&);

    // Move assignment
    Vector& operator=(Vector&&) noexcept;

    // Comparison operators
    bool operator==(const Vector&) const noexcept;
    inline bool operator!=(const Vector&) const noexcept;

    // Specific member functions...

  

    // Specific member functions (inherited from LinearContainer) ...

    // Specific member functions (inherited from SearchableContainer)...

};

/* ************************************************************************** */

#include "vector.cpp"

}

#endif

And there's the function implementations, the .cpp file:

vector.cpp

#include <stdexcept>

// Specific constructor
template <typename Data>
Vector<Data>::Vector(const unsigned long newsize) {
    Elements = new Data[newsize];
    size = newsize;
}

// Copy constructor
template <typename Data>
Vector<Data>::Vector(const Vector<Data>& vec) {
    Elements = new Data[vec.size];
    size = vec.size;
    for (unsigned long i = 0; i < size; i++) {
        Elements[i] = vec.Elements[i];
    }
}

// Move constructor
template <typename Data>
Vector<Data>::Vector(Vector<Data>&& vec) noexcept {
    std::swap(Elements, vec.Elements);
    std::swap(size, vec.size);
}

// Destructor
template <typename Data>
Vector<Data>::~Vector() {
    delete[] Elements;
}

// Copy assignment
template <typename Data>
Vector<Data>& Vector<Data>::operator=(const Vector<Data>& vec) {
    Vector<Data>* tmpvec = new Vector<Data>(vec);
    std::swap(*tmpvec, *this);
    delete tmpvec;
    return *this;
}

// Move assignment
template <typename Data>
Vector<Data>& Vector<Data>::operator=(Vector<Data>&& vec) noexcept {
    std::swap(Elements, vec.Elements);
    std::swap(size, vec.size);
}

// Comparison operators
template <typename Data>
bool Vector<Data>::operator==(const Vector<Data>& vec) const noexcept {
    if (size == vec.size) {
        for (unsigned long i; i < size; i++) {
            if (Elements[i] != vec.Elements[i]) {
                return false;
            }
        }
        return true;
    }
    else {
        return false;
    }
}

template <typename Data>
inline bool Vector<Data>::operator!=(const Vector<Data>& vec) const noexcept {
    return !(*this == vec);
}


}


Then I've defined a simple main to test the vector creation and... BOOM! Weird errors appear on the terminal.

main.cpp

#include "vector/vector.hpp"
int main()
{
    lasd::Vector<int> a(2);
    return 0;
}

Errors showing in compiling phase:

/usr/bin/ld: /tmp/ccNaRUyt.o: in function `lasd::Vector<int>::~Vector()':
main.cpp:(.text._ZN4lasd6VectorIiED1Ev[_ZN4lasd6VectorIiED1Ev]+0x4f): undefined reference to `lasd::SearchableContainer<int>::~SearchableContainer()'
/usr/bin/ld: /tmp/ccNaRUyt.o: in function `virtual thunk to lasd::Vector<int>::~Vector()':
main.cpp:(.text._ZN4lasd6VectorIiED1Ev[_ZN4lasd6VectorIiED1Ev]+0xb6): undefined reference to `lasd::SearchableContainer<int>::~SearchableContainer()'
/usr/bin/ld: /tmp/ccNaRUyt.o: in function `virtual thunk to lasd::Vector<int>::~Vector()':
main.cpp:(.text._ZN4lasd6VectorIiED1Ev[_ZN4lasd6VectorIiED1Ev]+0x116): undefined reference to `lasd::SearchableContainer<int>::~SearchableContainer()'
/usr/bin/ld: /tmp/ccNaRUyt.o: in function `virtual thunk to lasd::Vector<int>::~Vector()':
main.cpp:(.text._ZN4lasd6VectorIiED0Ev[_ZN4lasd6VectorIiED0Ev]+0x56): undefined reference to `lasd::SearchableContainer<int>::~SearchableContainer()'
/usr/bin/ld: /tmp/ccNaRUyt.o: in function `virtual thunk to lasd::Vector<int>::~Vector()':
main.cpp:(.text._ZN4lasd6VectorIiED0Ev[_ZN4lasd6VectorIiED0Ev]+0xc6): undefined reference to `lasd::SearchableContainer<int>::~SearchableContainer()'
/usr/bin/ld: /tmp/ccNaRUyt.o:main.cpp:(.text._ZN4lasd6VectorIiED0Ev[_ZN4lasd6VectorIiED0Ev]+0x12f): more undefined references to `lasd::SearchableContainer<int>::~SearchableContainer()' follow
collect2: error: ld returned 1 exit status

Community
  • 1
  • 1
  • *Container.cpp contains two function implementations that are not related to our issue.* -- You are mistaken. Where the implementation is coded is the reason for the error. Where are the implementations for all of those functions the linker is having issue with? More than likely, this question will be closed as a duplicate of "why template implementations go in header files". – PaulMcKenzie Apr 18 '20 at 13:21
  • SearchableContainer is a virtual class, so he doesn't have an implementation of the Destructor. Vector is a class that will be instantiated, so he got the Destructor implementation inside vector.cpp. – RalphTheCreator Apr 18 '20 at 13:25
  • 1
    *SearchableContainer is a virtual class, so he doesn't have an implementation of the Destructor* -- You declared a destructor, you didn't `default` the destructor, so it needs to be implemented, even if it's empty. – PaulMcKenzie Apr 18 '20 at 13:26
  • Arghh! I hate C++!! I forgot to assign `default` to the SearchableContainer class Destructor! – RalphTheCreator Apr 18 '20 at 13:29
  • The problem is that C++ error lines aren't easy to read as other programming languages. :( – RalphTheCreator Apr 18 '20 at 13:30

1 Answers1

0

Thanks to @PaulMcKenzie.

In the SearchableContainer class declaration (container.hpp) I've forgot to assign "= default" to the class Destructor.

Here's the change:

  // Destructor
    ~SearchableContainer(); 

to:

  // Destructor
    ~SearchableContainer() = default;