0

I have a Base class which I use as a Tree. From this Base class I derived a template Container class which should be able to hold various types. I would like to give the Container class a toString function, which converts its values and the values of all of its children to a string. A Container<A> can have a child of a different type Container<B>.

I don't know how to do this. Below is my code.

// File base.h:
class Base {
    public:
    virtual string toString();
    protected:
        vector<Base *> _children
}

// File base.cpp:
string Base::toString() {
    string a="something"
    return a;
}

Then I have a templated derived class:

// File container.h:
template<class T>
class Container: public Base {
    public:
        string toString() override;
    private:
        T _data;
}

I would like to specialize the toString function, so it can deal with different types:

File container.cpp:
template <class T>
string Container<T>::toString() {
    string valueString = "not sure how to handle this type";
    for(int i=0;i<_children.size;i++) {
        valueString+=" "+_children[i]->toString();
    }
    return valueString;
}
template <>
string Container<int>::toString() {
    string valueString = std::to_string(_data);
    for(int i=0;i<_children.size;i++) {
        valueString+=" "+_children[i]->toString();
    }
    return valueString;
}

I gave the Base class also a toString function, as I don't know how I would cast the _children to an unspecified Container class so I can access its toString function.

If I use the above approach, I get an error at linking:

undefined reference to Container<void*>::toString()
undefined reference to Container<bool*>::toString()

and for all the other types I ever used. However, I would like to avoid specializing all possible types.

Edit: As it has been suggested to move the content of container.cpp to the header file: If I do this, I get errors like this:

Multiple definition of Container<int>::toString().
First defined here

It seems basically that whereever I include Container.h I get such a multiple definition. This although I have a

#ifndef CONTAINER
#define CONTAINER

include guard?

maxwell
  • 357
  • 4
  • 14
  • your derived class is not inheriting from `Base`. Shouldn't it be `class Container:public Base` – Wander3r Aug 10 '18 at 08:36
  • You cannot put a template implementation (be it function template or class template) in a `.cpp` file and use it somewhere else! – Rerito Aug 10 '18 at 08:37
  • Yes, it should be Container::public Base, thank you, but that was not the problem – maxwell Aug 10 '18 at 08:39
  • See: https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file – Rerito Aug 10 '18 at 08:39
  • Thanks for the link. However, if I simply move the implementation in container.cpp to the end of Container.h I get errors of the kind "multiple definition of ContainertoString" first defined here..." – maxwell Aug 10 '18 at 08:54

1 Answers1

1

In C++ template only compiles when substituted.

In your situation, Container<T>::toString are defined in side container.cpp, but no substituted, so they won't be compiled.

When you reference to Container<T>::toString somewhere, the definition of the function is not visible to the compile unit, the compile will generate a relocation slot, hoping the definition will be find at link stage. But the function is never defined anywhere, so you got link error.

Solution: put the function definition to header file.

Here are some more detailed explanation.

Zang MingJie
  • 5,164
  • 1
  • 14
  • 27