c++c++17

Linker errors when inheriting from templated base class


I see the following errors reported by the linker when trying to compile the code below:

g++ -pedantic-errors -Wall -Wextra -Werror -std=c++17 -o <program_name> <object_dir/*.o> -L/usr/lib -lstdc++ -lm -I<include.dir>
/usr/bin/ld: /mnt/c/Users/AhmedRahim/workspace/repos/RvBaseLib/build/objects//mnt/c/Users/AhmedRahim/workspace/repos/RvBaseLib/source/main/RvBaseUInt64Property.o: in function `RvBaseUInt64Property::RvBaseUInt64Property(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, unsigned long)':
RvBaseUInt64Property.cpp:(.text+0x4e): undefined reference to `RvBaseProperty<unsigned long>::RvBaseProperty(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, unsigned long)'
/usr/bin/ld: /mnt/c/Users/AhmedRahim/workspace/repos/RvBaseLib/build/objects//mnt/c/Users/AhmedRahim/workspace/repos/RvBaseLib/source/main/RvBaseUInt64Property.o: in function `RvBaseUInt64Property::~RvBaseUInt64Property()':
RvBaseUInt64Property.cpp:(.text+0xc6): undefined reference to `RvBaseProperty<unsigned long>::~RvBaseProperty()'
/usr/bin/ld: /mnt/c/Users/AhmedRahim/workspace/repos/RvBaseLib/build/objects//mnt/c/Users/AhmedRahim/workspace/repos/RvBaseLib/source/tests/sanity_tests/sanity.o: in function `main':
sanity.cpp:(.text+0x84): undefined reference to `RvBaseProperty<unsigned long>::Print() const'
collect2: error: ld returned 1 exit status 

As far as I'm aware, there should not be any issues regarding inheriting from a templated class as of C++17. I'm not sure what is going wrong here. Can anyone help?

RvBaseProperty.h:

template <typename T> class RvBaseProperty
{
  private :
    string            mName {"Undefined"};    
    T                 mValue;
    bool              mValid {false};
    static uint64_t   mBasePropertyRefCount;

  public :
    explicit RvBaseProperty(string _name, T _value);
    ~RvBaseProperty();
    ... 
    void    Print() const;
};

RvBaseProperty.cpp - only relevant parts shown.

#include "RvBaseProperty.h"

using namespace std;

template <typename T> uint64_t RvBaseProperty<T>::mBasePropertyRefCount = 0;

// ========================================================================
template <typename T> RvBaseProperty<T>::RvBaseProperty(
  string  _name,
  T       _value) :
    mName   (_name),
    mValue  (_value)
// ========================================================================
{
  SetValid();
  ++mBasePropertyRefCount;
}

// ========================================================================
template <typename T> RvBaseProperty<T>::~RvBaseProperty()
// ========================================================================
{
  --mBasePropertyRefCount;
}

BaseUInt64Property.h

#include "RvBaseProperty.h"

class RvBaseUInt64Property : public RvBaseProperty<uint64_t>
{
  public:
    explicit RvBaseUInt64Property(string _name, uint64_t _value);
    ~RvBaseUInt64Property();
};

BaseUInt64Property.cpp

#include "RvBaseUInt64Property.h"

// ========================================================================
RvBaseUInt64Property::RvBaseUInt64Property(
  string    _name,
  uint64_t  _value) :
    RvBaseProperty<uint64_t>::RvBaseProperty(_name, _value)
// ========================================================================
{}

// ========================================================================
RvBaseUInt64Property::~RvBaseUInt64Property()
// ========================================================================
{}

main():

RvBaseUInt64Property const * prop0 = new RvBaseUInt64Property("uint64_t", 99);
prop0->Print();

Solution

  • You have to put your template definitions in the .h so that clients that instantiate your template can see the definition.

    Compilers do not instantiate (turn into object code) templates when they parse the definition as at that time the template parameters are not known. It is only when the template is used that the template parameters become known, and the template is turned into object code.

    This statement excludes explicit instantiation where the template parameter is explicitly supplied, which is not the case in your code.

    Kudos for not putting the template definitions inside of the template class declaration. That is an over-used feature of C++. Just put the definitions below the class declaration in the header file.