views:

66

answers:

3

I have a linker error I've reduced to a simple example. The build output is:

debug/main.o: In function main':
C:\Users\Dani\Documents\Projects\Test1/main.cpp:5: undefined reference to
log& log::operator<< (char const (&) [6])'
collect2: ld returned 1 exit status

It looks like the linker ignores the definition in log.cpp.
I also cant put the definition in log.h because I include the file alot of times and it complains about redefinitions.

main.cpp:

#include "log.h"

int main()
{
    log() << "hello";
    return 0;
}

log.h:

#ifndef LOG_H
#define LOG_H

class log
{
public:
    log();
    template<typename T>
    log &operator <<(T &t);
};

#endif // LOG_H

log.cpp:

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

log::log()
{
}

template<typename T>
log &log::operator <<(T &t)
{
    std::cout << t << std::endl;
    return *this;
}
A: 

When you create a template, you should either put definition in the header or use export keyword in cpp. Unfortunately, many(almost all) compilers just ignore export in such context, so the only option you have is to put definition into your header file.

a1ex07
`export` was removed from the new c++ standard. Not just deprecated like `auto_ptr`, removed.
caspin
+1  A: 

You have two standard options:

  1. Define the operator inside the header file

  2. Explicitly instantiate the template

adf88
How can I define the operator inside the header file and not face redefinition problems?
Dani
C++ gives templates special semantics so that you won't get redefinition problems.
caspin
+1  A: 

Hi Dani.

I guess this is your first use of templates, so I'll try to be didactic.

You can think of template as some kind of type-aware macros. This type awareness is of course not to be neglected, since it grants type safety for free. This does mean however than template functions or classes are NOT functions or classes: they are model that will be used to generate functions or classes.

For example:

template <class T>
void foo(T t) { std::cout << t << "\n"; }

This is a template function, it allows me to define something once and apply it to many different types.

int i;
foo(i); // [1]

This causes the instantiation of the template. Basically, it means that a function is created according to the model, but replacing all occurrences of T by int.

double d;
foo(d);    // Another instantiation, this time with `T` replaced by `double`
foo(d);    // foo<double>() already exists, it's reused

Now, this idea of model is very important. If the definition of the model is not present in the header file, then the compiler does not know how to define the method.

So, you have 2 solutions here:

  1. Define it in the header
  2. Explicitly instantiate it

The 2 have different uses.

(1) is the classic way. It's easier because you don't restrict the user to a subset of the types. However it does mean that the user depends on the implementation (change it, she recompiles, and you need to pull the dependencies in the header)

(2) is less often used. For full compliance with the standard it requires:

  • That you declare the specialization in the header (template <> void foo<int>();) so as to let the compiler know it exists
  • That you fully define it in one of the translation units linked

The main advantage is that, like classic functions, you isolate the client from the implementation.

gcc is quite lenient because you can forgo the declaration and it should work.

I should also note that it is possible to define a method twice, with different implementations. This is of course an error, as it is in direct violation of the ODR: One Definition Rule. However most linkers don't report it because it's quite common to have one implementation per object, and they just pick the first and assume the others will be equivalent (it's a special rule for templates). So if you do want to use explicit instantiation, take great care to only define it once.

Matthieu M.
I've used templates before, but I always thought the compiler just uses the class and "replaces" the typename.Thanks for this explanation.
Dani