10

I want to use iterators in template class method. Here is my code: (testclass.h)

template<typename T, typename container>
class TestClassX
{
public:
    void gen(typename container::iterator first );
};

and file testclass.cpp:

template<typename T, typename container>
void TestClassX<T, container>::gen(typename container::iterator first)
{

}

When i try to run it:

TestClassX<unsigned, std::vector<unsigned> > testx;
testx.gen(it);

I get an error:

Error:undefined reference to `TestClassX<unsigned int, std::vector<unsigned int, std::allocator<unsigned int> > >::gen(__gnu_cxx::__normal_iterator<unsigned int*, std::vector<unsigned int, std::allocator<unsigned int> > >)'

I use mingw32 4.4

I want to have a class that can write to different containers like std::vector, std::list, QVector or QList all that have STL-style iterators.

pmr
  • 58,701
  • 10
  • 113
  • 156
seb6k
  • 103
  • 1
  • 4
  • possible duplicate of [Undefined reference to template members](http://stackoverflow.com/questions/4100893/undefined-reference-to-template-members) – kennytm Feb 19 '12 at 19:06

5 Answers5

13

Template class methods must be defined in the header file. When you use a template class, the compiler actually compiles a version of that class for the given template parameters. Therefore, it is a requirement that the body of each method is available when including the header file.

Remove you source file and include the body in testclass.h:

template<typename T, typename container>
class TestClassX
{
public:
    void gen(typename container::iterator first ) {

    }
};
mfontanini
  • 21,410
  • 4
  • 65
  • 73
  • Template class methods doesn't need to be defined in the header file. You just need to let linker find it. Agreed with @Thaddeus – eric-haibin-lin Apr 08 '17 at 05:02
4

Template class methods NEED NOT be defined in the header files. But if you do this you need to define a separate compilation unit (for example templates.cpp) and in that you include the source code file of the template class (eg. #include "container.cpp" // the .cpp NOT the .hpp file) then you need to define the instances of the templates that you are using (eg. template class Container;). You also need to define the object for the template class (eg Link). In this particular case, since we are using a pointer to this object (eg Link*, in Containter ) we merely need to 'forward declare' that object.

Here is the full template.cpp file. Which you would compile and link in with the rest of the code.

class Link;
#include "Container.cpp"    // use the source code, not the header
template class Container<Link*>; 

I like using this method because it prevents the compiler from generating template class instances automagically and lets you know when it can't find it.

Compile with gcc using the option -fno-implicit-templates.

When you build everything will be compiled as normal but then the collector will recompile the templates.cpp file for all the objects that use the template.

Thaddeus
  • 84
  • 6
0

As previously stated, the definition must exist in the same compilation unit when the template is instantiated.

I personally prefer to keep the definitions separated from the declarations.
This keeps the header files cleaner and visually separates the interface from the implementation.

One solution can therefore be as follows:

//TestClass.hpp

//Interface:
template<typename T>
class TestClassX
{
public:
    void gen(int a);
    //more declaraions...
}; 

//Implementations:
template<typename T>
void TestClassX<T>::gen(int a)
{
    //beautiful code
}

You could place the implementation and the interface in separate files (i.e TestClass.hpp and ITestClass.hpp respectively).
TestClass.hpp will initially #include ITestClass.hpp and then define the function as shown in the example above.
The clients will then only need to #include TestClass.hpp.

Daniel
  • 1,319
  • 14
  • 19
0

May be worth looking at this answer over here to see if inline is appropriate for you. https://stackoverflow.com/a/51585746/1440598

devanl
  • 1,232
  • 1
  • 10
  • 20
0

Its technically possible to do this, you just need to explain to the linker where the implementation can be found, but in real world experience, this is seldom done as its requires allot of boiler plate and disallows you to use constexpr variables from another file in both the .hpp and .cpp as the compiler will complain about redefinition use.

One way i personally solve this is by using a helper function in another header file and call it from the base header file. For example lets say we have a class Encoder in encoder.hpp than we can create another file called encoder_impl.hpp and do the following.

encoder_impl.hpp

#include ... // include what you need. 
enum class ENCODE_TYPE : u8 { ... };
enum class CONN_TYPE : u8 { ... };

namespace encoder_impl {

 template<ENCODE_TYPE E, CONN_TYPE C>
 static inline void helper(...){...};

 template<> void helper<ENCODE_TYPE::LOGON, CONN_TYPE::QUOTE>
 (...){...}; 

} // encoder_impl

encoder.hpp

#include ... // include what you need. 
#include "encoder_impl.hpp"

namespace encoder {

template <CONN_TYPE C>
struct Encoder{

  template<ENCODE_TYPE E>
  inline void encode(...){
    encoder_impl::helper<E, C>(...);
  }

  inline buffer_conn<C>& get_buff() const { return buff; };

private:
  buffer_conn<C> buff; //just an example needing CONN_TYPE

} // encoder

This does have the downside that your 'implementation' file will also re-include everything when the normal .hpp file is used, but this does not really affect run-time performance only compile time.