October 22, 2024
Chicago 12, Melborne City, USA
templates

Why can templates only be implemented in the header file?


I’m creating my own C++ tutorial, and I’m immersed in providing some template examples.
I know that the C++ Standard Library contains a Stack class, but I’m trying to provide relatively simple examples that don’t rely on using [much of] the C++ Standard Library.

I created a version of my own example template Stack class:

#ifndef TemplStack_h
#define TemplStack_h

template <typename TYPE>
class Stack
{
public:
  Stack(size_t s)
    : m_base(new TYPE[m_size = s])
  {
    m_top = m_base;
  }
  
  ~Stack()
  {
    delete [] m_base;
  }
  
  void Push(const TYPE a)
  {
    *m_top++ = a;
  }
  
  TYPE Pop()
  {
    return *--m_top;
  }
  
  size_t Size() const
  {
    return m_top - m_base;
  }

private:
  TYPE*   m_base; // The stack
  TYPE*   m_top;  // The current top
  size_t  m_size; // The size of the stack
};

#endif /* TemplStack_h */

and tested it with this main program:

#include <iostream>
#include "TemplStack.h"

int main(int argc, const char * argv[])
{
  const char* values[] = {"Mabel","George","Joe"};

  Stack<const char *> mystack(100);
  
  for (int i = 0; i < 3; i++)
  {
    mystack.Push(values[i]);
  }
  std::cout << "Size = "
            << mystack.Size() << std::endl
            << "Values: ";
  
  for (int i = 0; i < 3; i++)
  {
    std::cout << mystack.Pop() << ' ';
  }
  std::cout << std::endl;

  return 0;
}

It outputs the expected results.

So, then, I wanted to explore separating out the template class methods: declaration in the header file, definition in a separate .cpp file.

Here’s my new header file:

#ifndef TemplStack_h
#define TemplStack_h

template <typename TYPE>
class Stack
{
public:
  Stack(size_t s);
  ~Stack();
  void Push(const TYPE a);
  TYPE Pop();
  size_t Size() const;

private:
  TYPE*   m_base; // The stack
  TYPE*   m_top;  // The current top
  size_t  m_size; // The size of the stack
};

#endif /* TemplStack_h */

and the corresponding .cpp file:

#include <iostream>
#include "TemplStack.h"

template <typename TYPE>
Stack<TYPE>::Stack(size_t s)
    : m_base(new TYPE[m_size = s])
{
  m_top = m_base;
}
  
template <typename TYPE>
Stack<TYPE>::~Stack()
{
  delete [] m_base;
}
  
template <typename TYPE>
void Stack<TYPE>::Push(const TYPE a)
{
  *m_top++ = a;
}
  
template <typename TYPE>
TYPE Stack<TYPE>::Pop()
{
  return *--m_top;
}

template <typename TYPE>
size_t Stack<TYPE>::Size() const
{
  return m_top - m_base;
}

The main program remained the same.

However, when I run this, I get several linker errors, and I’m very mystified by why they occur.

I’m using XCode on an iMac with compiler Apple Clang, GNU++20.

Here are the linker errors I’m getting:

ld: Undefined symbols:
  Stack<char const*>::Pop(), referenced from:
      _main in main.o
  Stack<char const*>::Push(char const*), referenced from:
      _main in main.o
  Stack<char const*>::Stack(unsigned long), referenced from:
      _main in main.o
  Stack<char const*>::~Stack(), referenced from:
      _main in main.o
      _main in main.o
  Stack<char const*>::Size() const, referenced from:
      _main in main.o

It seems that the functions I defined in TemplStack.cpp aren’t being found.

I tried modifying the main program to:

#include <iostream>
#include "TemplStack.h"

int main(int argc, const char * argv[]) 
{
  Stack<int> mystack(100);
/*
  const char* values[] = {"Mabel","George","Joe"};

  Stack<const char *> mystack(100);
  
  for (int i = 0; i < 3; i++)
  {
    mystack.Push(values[i]);
  }
  std::cout << "Size = "
            << mystack.Size() << std::endl
            << "Values: ";
  
  for (int i = 0; i < 3; i++)
  {
    std::cout << mystack.Pop() << ' ';
  }
  std::cout << std::endl;
 */

  return 0;
}

That now produces the following link time errors:

ld: Undefined symbols:
  Stack<int>::Stack(unsigned long), referenced from:
      _main in main.o
  Stack<int>::~Stack(), referenced from:
      _main in main.o

which is even more mysterious, since my main program does not reference an unsigned long.

In case of a possible XCode configuration problem, I tried this in https://onecompiler.com/cpp/42v7wdnvw with the same results.

Any help would be appreciated. I’m interesting not only in solving the problem, but in particular why things are happening this way — I’m creating a tutorial, so it’s important for me to understand the why as well as the what.

Thanks!



You need to sign in to view this answers

Leave feedback about this

  • Quality
  • Price
  • Service

PROS

+
Add Field

CONS

+
Add Field
Choose Image
Choose Video