undefined reference to问题的一种可能情况以及问题发生的原因

问题描述

报错:undefined reference to xxx
根据经验,这种一般都是ld链接错误,大部分情况都是忘了链接某个文件或者这个函数没有定义。

当然,也有可能是因为实现了函数重载,但是没有重载该参数组合的函数。

解决办法也很简单,查看一下该函数有没有定义,如果定义了查看一下CMakeList里面是否链接了该函数所在的文件,基本上问题就解决了。

如果没有用Cmake,那就直接修改g++编译命令,如果第三方库后面加上-l 以及所需的库名,如果是个人所写函数就直接加上文件名。
eg:g++ -o output file1.cpp file2.cpp -lpthread

但是今天遇见了一个奇怪的现象,函数在头文件中定义了,头文件有include了,cpp文件中也对该函数进行了实现,而且也链接了该函数,但是在编译时就是报错undefined reference。
问题复现代码:

// Aclass.hpp
#pragma once
#include <iostream>

template <typename T>
class A{
	private:
        T num;
    public:
        A() {};
        ~A() {};
        void func1() {};
        void func2();
        void func3();
};

// Aclass.cpp
#include "Aclass.hpp"

template <typename T>
void A<T>::func2(){
    std::cout << "I am func2 & written in cpp!" <<std::endl;
}

template <typename T>
void A<T>::func3(){
    std::cout << "I am func3 & written in cpp!" <<std::endl;
}

// main.cpp
#include "Aclass.hpp"

int main(){
    A<int> TestA = A(0);
    TestA.func1();
    TestA.func2();
    TestA.func3();
    return 0;
}

编译指令:

g++ main.cpp Aclass.cpp

问题分析

报错的是一个类的成员函数,该类的声明在Matrix.hpp,函数定义在Matrix.cpp,查看报错发现并不是所有的成员函数都会报错,而是只有在cpp中的类外实现的函数才会报错,在hpp里实现的函数就不会报错.

后来发现,问题出在模板类上,模板类的函数和类的定义不能分到两个文件中,否则就会出现这个报错。

原理

模板类的编译分为两个环节完成:

  • 第一个环节进行词法检查,主要检查模板代码是否有语法错误等,这个阶段并不涉及模板实例化。
  • 第二个阶段是在发生实际调用时,会将模板实例化为对应的类,如果函数和模板定义分开了,当进行编译main文件时,发现需要实例化模板类,但是hpp中没有模板类函数的实现,就会报错

因为模板类函数的实现在cpp文件中,而cpp文件是分开进行编译的,只有在链接时才会组合到一起
也就是说,模板类相比于普通类,需要更早知道实现代码,
普通类/函数只需要在链接时知道找到实现代码即可
模板类/函数在进行编译调用该类/函数的文件时就需要知道实现代码,而由于cpp文件的独立编译,这又是不可能实现的。
所以,只能将模板类/函数的定义和实现放到一起。

在编译器进行编译的时候,编译器会产生类的模板函数的声明,当时实际确认类型后调用的时候,会根据调用的类型进行再次帮我们生成对应类型的函数声明和定义。我们称之为二次编译。同样,因为这个机制,会经常报错找不到类的函数的实现。在模板类的友元函数外部定义时,也会出现这个错误。

解决办法

几种解决办法:

  • 将模板类的定义和函数写到一个hpp文件中
  • 在hpp文件中定义全局函数,调用具体类型的模板类,但是这样的话还写模板干什么?也太丑了
  • 写两个hpp文件,一个hpp中包括定义,一个hpp中包括函数实现,函数实现的hpp中include模板类定义的hpp,需要用到模板类的文件再include函数实现的hpp.

第三种方法其实跟第一种是一样的,只不过是写到两个里面,利用预处理将其合到一起,同样的,直接include cpp文件也行,只不过过于丑陋

模板类/函数不能分开定义,在C++ primer 16章中有讲到,在C++新经典中也有提到,呜呜呜,学艺不精,回家种地。

### C语言中 `undefined reference to` 错误解决方案 #### 函数未定义 当遇到 `undefined reference to` 的错误提示时,可能是因为目标函数并未被定义。确保所有使用的函数都在源码中有实现[^1]。 ```c // 示例:确保 main.c 中有 foo() 实现 void foo() { printf("Function is defined\n"); } ``` #### 链接时缺少对象文件 如果项目由多个源文件组成,则需确认链接阶段包含了所有的 `.o` 或者 `.obj` 文件。遗漏任何必要的对象文件都会引发此类错误。 #### 缺失库文件 对于标准库之外的功能调用,比如网络编程中的 socket API ,应该指定相应的外部库来提供这些功能的支持。例如,在 Linux 下使用 `-lsocket` 参数;而在 Windows 平台上则可能是 winmm.lib 这样的媒体控制接口库[^2]。 #### 动态/静态库问题 某些情况下,程序试图访问位于共享库 (.so, .dll) 或者静态库 (.a, .lib) 内部的符号却失败了。这通常意味着编译器无法找到所需的库路径或者是版本冲突所致。检查并修正项目的依赖配置可以解决问题。 #### 库链接顺序不当 在 GCC/G++ 命令行里添加库的时候要注意它们之间的相对位置关系。因为链接过程是从左向右依次处理输入项,所以应当把自定义模块放在第三方库之前。 #### 跨平台开发注意事项 不同操作系统上相同名称但行为有所差异的标准库也可能造成此现象。特别是移植代码至新环境时要特别留意这一点。 #### 不兼容的 ABI (应用程序二进制接口) 有时即使两个库都实现了同样的接口,但由于内部数据结构布局的不同也会导致类似的连接期故障。此时建议升级工具链或尝试寻找更稳定的替代品。 #### 名称修饰差异 C++ 和其他一些高级语言会对导出的名字做额外编码(即所谓的 "name mangling"),而纯 C 则不会这样做。因此混合编程环境下容易发生匹配不上原始声明的情形。可以通过 extern “C” 来强制保持一致性的命名约定。 #### 符号可见性设置 最后还有一种情况是在构建过程中有意隐藏了一些全局变量或方法从而阻止外界直接引用。查阅文档了解如何调整相关属性以便让特定条目对外界可用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值