模板运算符链接器错误
我有一个链接器错误,我已简化为一个简单的示例.构建输出是:
I have a linker error I've reduced to a simple example. The build output is:
debug/main.o: 在函数 main':
log&的引用日志::运算符<<(字符const (&) [6])'
C:UsersDaniDocumentsProjectsTest1/main.cpp:5:未定义的对
collect2: ld 返回1 退出状态
debug/main.o: In function
main':
log& log::operator<< (char const (&) [6])'
C:UsersDaniDocumentsProjectsTest1/main.cpp:5: undefined reference to
collect2: ld returned 1 exit status
链接器似乎忽略了 log.cpp 中的定义.
我也不能将定义放在 log.h 中,因为我多次包含该文件并且它抱怨重新定义.
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;
}
推荐答案
我猜这是你第一次使用模板,所以我会尽量说教.
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.
例如:
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]
这会导致 template
的实例化.基本上就是按照模型创建一个函数,但是把所有出现的T
替换成int
.
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.
所以,这里有 2 个解决方案:
So, you have 2 solutions here:
- 在标题中定义
- 显式实例化它
两者有不同的用途.
(1) 是经典的方式.这更容易,因为您不会将用户限制为类型的子集.但是,这确实意味着用户依赖于实现(更改它,她重新编译,并且您需要在标题中拉取依赖项)
(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) 很少使用.为完全符合其要求的标准:
(2) is less often used. For full compliance with the standard it requires:
- 您在标头中声明特化(
template <> void foo<int>();
)以便让编译器知道它存在 - 您在链接的翻译单元之一中完全定义它
- 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
非常宽松,因为您可以放弃声明,它应该可以工作.
gcc
is quite lenient because you can forgo the declaration and it should work.
我还应该注意,可以使用不同的实现定义两次方法.这当然是一个错误,因为它直接违反了 ODR:一个定义规则.然而,大多数链接器不会报告它,因为每个对象都有一个实现是很常见的,他们只是选择第一个并假设其他的将是等效的(这是模板的特殊规则).因此,如果您确实想使用显式实例化,请务必只定义一次.
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.
相关文章