CMake编译通过但链接失败:模板函数已定义仍报未引用错误
CMake编译C++链接失败问题解决(模板函数导致未定义引用)
问题情况
初次使用CMake编译C++程序,编译阶段成功但链接阶段失败,已实现相关定义但找不到原因。相关代码及错误信息如下:
main.cpp
#include <iostream> #include <map> #include <vector> #include <fstream> #include <string> #include "file_management.hpp" #include "data_retrieval.hpp" #include "data_writing.hpp" using namespace std; int main(){ map<string ,string> package_map; map<string ,string> map; map["abc"] = "123"; map["luv"] = "123"; map["you"] = "self"; {//outputting to testfile ofstream testfile{"doc/testfile.txt"}; check_open(testfile); deposit_map(map, testfile); testfile.close(); } ifstream testfile{"doc/testfile.txt"}; check_open(testfile); retrieve_map(package_map, testfile); for (const auto& item : package_map){ cout << item.first << '\t' << item.second << '\n'; } return 0; }
data_writing.hpp
#ifndef DATA_WRITING #define DATA_WRITING template <typename T> void deposit_vector(std::vector<T> source_vector, std::ofstream& output_file); template <typename T1, typename T2> void deposit_map(std::map<T1,T2> source_map, std::ofstream& output_file); #endif
data_writing.cpp
#include <fstream> #include <map> #include <vector> #include "file_management.hpp" template <typename T1, typename T2> void deposit_map(std::map<T1,T2> source_map, std::ofstream& output_file){ for( const auto& item : source_map){ output_file << item.first << '\t' << item.second << '\n'; } } template <typename T> void deposit_vector(std::vector<T> source_vector, std::ofstream& output_file){ for( const auto& x : source_vector){ output_file << x << '\n'; } }
CMakeLists.txt
cmake_minimum_required(VERSION 3.20) project(Post_Transition CXX) add_executable(Ptran data_retrieval.cpp data_writing.cpp file_management.cpp main.cpp package.cpp)
链接错误信息
CMakeFiles\Ptran.dir/objects.a(main.cpp.obj):main.cpp:(.text+0x1d4): undefined reference to `void deposit_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::basic_ofstream<char, std::char_traits<char> >&)'
问题原因与解决方法
这是C++模板函数的典型编译链接问题:模板函数的实现不能单独放在.cpp源文件中,因为C++编译器在编译阶段需要看到模板的完整定义才能生成特定类型的实例化代码。
编译main.cpp时,编译器看到deposit_map<string, string>的调用,但只有头文件中的模板声明,没有实现,无法生成对应的函数代码;而编译data_writing.cpp时,编译器不知道需要实例化哪些类型的模板,也不会生成对应的函数代码,最终链接阶段就会出现"未定义引用"错误。
有两种简单的解决方法:
方法一:将模板函数的实现移到头文件中
修改data_writing.hpp,把data_writing.cpp中的实现直接放到头文件里:
#ifndef DATA_WRITING #define DATA_WRITING #include <fstream> #include <map> #include <vector> template <typename T> void deposit_vector(std::vector<T> source_vector, std::ofstream& output_file){ for( const auto& x : source_vector){ output_file << x << '\n'; } } template <typename T1, typename T2> void deposit_map(std::map<T1,T2> source_map, std::ofstream& output_file){ for( const auto& item : source_map){ output_file << item.first << '\t' << item.second << '\n'; } } #endif
之后可以删除data_writing.cpp文件,或者保留但清空内容(避免重复定义)。
方法二:在模板实现文件中显式实例化需要的类型
如果不想把实现移到头文件,就在data_writing.cpp末尾添加显式实例化代码,告诉编译器生成特定类型的模板函数:
// 在data_writing.cpp最后添加 template void deposit_map<std::string, std::string>(std::map<std::string, std::string>, std::ofstream&); template void deposit_vector<std::string>(std::vector<std::string>, std::ofstream&); // 如果还有其他需要的类型,也需要显式实例化
这种方法的缺点是后续新增模板类型调用时,都需要手动添加对应的显式实例化代码。
内容的提问来源于stack exchange,提问作者Hoonter
相关产品推荐
相关产品推荐

