带自定义删除器的unique_ptr引发目标文件警告与链接错误
场景:为大型程序生成目标文件,使用带自定义删除器的unique_ptr类型,编译命令为:
g++ -std=c++20 tmp.cpp -c -Wall
tmp.cpp代码:
#include <memory> using specialptr = std::unique_ptr<int, decltype([](int *f) { delete f; })>; specialptr fun(){ return specialptr{new int}; }
编译tmp.cpp时出现警告:
tmp.cpp:12:12: warning: ‘specialptr fun()’ defined but not used [-Wunused-function]
12 | specialptr fun(){ return specialptr{new int};}
|
将该目标文件与调用fun()的main.cpp链接时,出现错误:
In file included from main.cpp:1:
tmp.h:19:12: warning: ‘specialptr fun()’ used but never defined
19 | specialptr fun();
| ^~~
/usr/bin/ld: /tmp/ccnuTTGC.o: in functionmain': main.cpp:(.text+0x4c): undefined reference tofun()'
collect2: error: ld returned 1 exit status
将返回值改为普通指针时代码可正常运行,但已在头文件中声明类型与函数,请问问题出在哪里?使用g版本:g (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
问题原因与解决方法
核心原因
C++中每个lambda表达式的类型都是唯一的匿名类型。你在tmp.cpp中用decltype([](int* f){delete f;})定义的specialptr类型,和main.cpp通过头文件引入的specialptr类型(若头文件采用相同lambda写法)是完全不同的类型。这导致fun()在tmp.cpp中的函数签名(返回自定义specialptr)与main.cpp中声明的签名不匹配,链接器无法找到对应函数实现,从而报"undefined reference"错误。
而普通指针int*是标准类型,所有翻译单元中的类型完全一致,函数签名匹配,因此链接正常。
解决办法
方法1:使用具名函数/函数指针作为删除器类型
将删除器改为普通函数,用函数指针类型作为unique_ptr的模板参数,确保所有翻译单元类型一致:
// tmp.h #include <memory> void custom_deleter(int* f) { delete f; } using specialptr = std::unique_ptr<int, void(*)(int*)>; specialptr fun();
// tmp.cpp #include "tmp.h" specialptr fun(){ return specialptr{new int, custom_deleter}; }
方法2:使用inline变量共享lambda类型
将lambda定义为inline全局变量,确保所有翻译单元共享同一个lambda实例,其decltype类型也会一致:
// tmp.h #include <memory> inline auto deleter = [](int* f) { delete f; }; using specialptr = std::unique_ptr<int, decltype(deleter)>; specialptr fun();
// tmp.cpp #include "tmp.h" specialptr fun(){ return specialptr{new int, deleter}; }
方法3:使用std::function封装删除器
用std::function作为删除器类型,它是通用的可调用对象包装器,能统一不同实现的类型:
// tmp.h #include <memory> #include <functional> using specialptr = std::unique_ptr<int, std::function<void(int*)>>; specialptr fun();
// tmp.cpp #include "tmp.h" specialptr fun(){ return specialptr{new int, [](int* f){delete f;}}; }
内容的提问来源于stack exchange,提问作者Trick

