5

I have some code that looks like this in an Array.hpp file

template <typename T>

class Array {
   private:
    T *_array;
    int _arrSize;

   public:
    Array<T>();
    Array<T>(unsigned int n);
...
};

then on the Array.cpp file I have like this

#include "Array.hpp"

template <typename T>
Array<T>::Array<T>(){};

template <typename T>
Array<T>::Array<T>(unsigned int n) : _arrSize(n) {
    T *a = new T[n];
    for (unsigned int i = 0; i < n; i++) {
        a[i] = 0;
    }
    this->_array = a;
};
...

then a main as simple as

int main() {
    Array<int> g(2);
    return 0;
}

but when I try to compile this with clang++ -Wall -Wextra -Werror -std=c++98 *.c I get this error

Array.cpp:16:11: error: out-of-line constructor for 'Array' cannot have template arguments
Array<T>::Array<T>(){};
          ^    ~~~
Array.cpp:19:11: error: out-of-line constructor for 'Array' cannot have template arguments
Array<T>::Array<T>(unsigned int n) : _arrSize(n) {
          ^    ~~~

I'm not sure what I'm doing wrong

vlad_tepesch
  • 6,681
  • 1
  • 38
  • 80
sassy_rog
  • 1,077
  • 12
  • 30

2 Answers2

7

your definitions prototype is wrong:

template <typename T>
Array<T>::Array<T>(){};

the Visual Studio 2015 compiler issues this warning that make the error a bit more clear:

warning C4812: obsolete declaration style: please use 'Array<T>::Array' instead

so your implementation should look like:

template <typename T>
Array<T>::Array(){};

template <typename T>
Array<T>::Array(unsigned int n) : _arrSize(n) {
    ...
};
...

This is because the constructor itself is not "templated".

If the declaration would look like.

template <typename T>
class Array<T> 
{
    template <typename Y>
    Array<T>(Y n);
};

you would require this additional template argument, but with another template line:

template <typename T>
template <typename Y>
Array<T>::Array<Y>(Y n){};

However one more problem of your code is that you hide the template implementation from the the including files, so the compiler can not instantiate it.

Currently your main.cpp is unable to see the constructor implementations. it only sees the declaration in the header.

You have different options:

  1. you restrict your class to be used with a strict subset of types only by explicitly instantiate the template in your Array.cpp: with directives like: template class Array<int>; at file scope. For a container type this does not seem to be an option.
  2. you move the implementations into your header file.
  3. if you want to separate declaration and implementation you can include the Array.cpp (but i would call it Array_Impl.hpp at the end of your header (but you have to be careful, because everything in the cpp will be visible in the header and for all files that include your header)
vlad_tepesch
  • 6,681
  • 1
  • 38
  • 80
  • Tried this and I get an undefined symbol error `Undefined symbols for architecture x86_64 . "Array::Array(unsigned int)", referenced from: _main in main-8d1f60.o` – sassy_rog Jun 14 '19 at 09:46
  • 1
    @SASSY_ROG yes, that is the next problem. you cannot write template code into a own translation unit. all the templates code has to be visible by the translation unit that instantiates the template - that does mean, that you should write in the header too or include the cpp (i would chose a different ending) file from the header if you want to separate declarations and implementations – vlad_tepesch Jun 14 '19 at 10:00
6

The correct syntax is

Array<T>::Array()

not

Array<T>::Array<T>()

On a related note, implementing your templates in .cpp files is almost always wrong.

You should be using std::vector anyway.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243