Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
365 views
in Technique[技术] by (71.8m points)

c++ - Template operator linker error

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

debug/main.o: In function main':
C:UsersDaniDocumentsProjectsTest1/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;
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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 << "
"; }

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...