【问题标题】:Template operator linker error模板运算符链接器错误
【发布时间】:2011-03-01 17:43:08
【问题描述】:

我有一个链接器错误,我已简化为一个简单的示例。 构建输出是:

debug/main.o:在函数main':
C:\Users\Dani\Documents\Projects\Test1/main.cpp:5: undefined reference to
log& 日志::运算符 collect2: ld 返回 1 退出状态

链接器似乎忽略了 log.cpp 中的定义。
我也不能将定义放在 log.h 中,因为我多次包含该文件并且它抱怨重新定义。

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

【问题讨论】:

标签: c++ templates linker


【解决方案1】:

我猜这是你第一次使用模板,所以我会尽量说教。

您可以将模板视为某种类型感知的宏。这种类型意识当然不容忽视,因为它免费提供类型安全。然而,这确实意味着模板函数或类不是函数或类:它们是用于生成函数或类的模型。

例如:

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

这是一个模板函数,它允许我定义一次并将其应用于许多不同的类型。

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

这会导致template 的实例化。基本上就是根据模型创建一个函数,但是将所有出现的T替换为int

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

现在,这个模型的想法非常重要。如果头文件中没有模型的定义,则编译器不知道如何定义方法。

所以,这里有 2 个解决方案:

  1. 在标题中定义
  2. 显式实例化它

两者有不同的用途。

(1) 是经典的方式。这更容易,因为您不会将用户限制为类型的子集。但是,这确实意味着用户依赖于实现(更改它,她重新编译,并且您需要在标题中提取依赖项)

(2) 很少使用。为完全符合其要求的标准:

  • 您在标头中声明特化 (template &lt;&gt; void foo&lt;int&gt;();) 以便让编译器知道它存在
  • 您在链接的翻译单元之一中完全定义它

主要优点是,与经典函数一样,您可以将客户端与实现隔离开来。

gcc 非常宽松,因为您可以放弃声明,它应该可以工作。

我还应该注意,可以使用不同的实现两次定义一个方法。这当然是一个错误,因为它直接违反了 ODR:一个定义规则。然而,大多数链接器不会报告它,因为每个对象都有一个实现是很常见的,他们只是选择第一个并假设其他的将是等效的(这是模板的特殊规则)。因此,如果您确实想使用显式实例化,请务必只定义一次。

【讨论】:

  • 我以前使用过模板,但我一直认为编译器只是使用类并“替换”类型名。感谢您的解释。
【解决方案2】:

当您创建模板时,您应该将定义放在标题中或在 cpp 中使用 export 关键字。不幸的是,许多(几乎所有)编译器在这种情况下只是忽略export,所以您唯一的选择是将定义放入您的头文件中。

【讨论】:

  • export 已从新的 c++ 标准中删除。不仅像 auto_ptr 那样被弃用,还被删除。
【解决方案3】:

您有两个标准选项:

  1. 在头文件中定义操作符

  2. Explicitly instantiate the template

【讨论】:

  • 如何在头文件中定义操作符而不遇到重定义问题?
  • C++ 为模板提供了特殊的语义,这样您就不会遇到重新定义的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-28
  • 1970-01-01
  • 1970-01-01
  • 2010-10-18
  • 1970-01-01
  • 2015-09-02
相关资源
最近更新 更多