提问人:user366312 提问时间:10/20/2023 更新时间:10/20/2023 访问量:57
模板函数多定义错误 [重复]
Template function multiple definition error [duplicate]
问:
在Linux中开发了一个巨大的基于cmake的C++项目。我正在尝试在 Windows 10 中编译它。
当我尝试使用 MSYS2 64 位和 VSCode 构建它时,我收到以下链接错误。
[build] [ 98%] Building CXX object CMakeFiles/heca_input_exe.dir/utils/options/structures_from_cmdline.cc.obj
[build] [100%] Linking CXX executable heca_input_exe.exe
[build] C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles\heca_input_exe.dir/objects.a(string_utils.cc.obj): in function `std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >& utils::split<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, std::vector<char, std::allocator<char> > const&, bool, bool)':
[build] C:/Users/pc/Documents/__protein design/___hydrogen bond/BioshellHydrogenBond/utils/string_utils.cc:62: multiple definition of `std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >& utils::split<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, std::vector<char, std::allocator<char> > const&, bool, bool)'; CMakeFiles\heca_input_exe.dir/objects.a(DataTable.cc.obj):C:/Users/pc/DOCUME~1/__protein design/___hydrogen bond/BioshellHydrogenBond/utils/string_utils.hh:140: first defined here
该函数的声明和定义如下:split()
//File: string_utils.hh
//
// ... ... ... ...
//
/** @brief Splits a string into tokens based on a given delimiter.
*
* @param str - the input string with tokens to be converted
* @param delim - character to be trimmed off (a space by default)
* @param if_trim_tokens - if true, tokens will be trimmed of white space characters
* @return the newly created vector that holds the tokens extracted from the given string
*/
std::vector<std::string> split(const std::string &str, const std::vector<char> & delim = {' '}, const bool if_trim_tokens = true,const bool if_trim_line = true);
/** @brief Splits a string into a vector of tokens based on a given delimiter.
*
* @param input_str - input string
* @param separator - separator (delimiter)
* @param max - maximum number of tokens to be extracted
* @param results - vector to insert the resulting tokens
*/
void split(const std::string &input_str, const std::string &separator, const int max, std::vector<std::string> &results);
//
// ... ... ... ...
//
/** @brief Splits a string into tokens based on a given delimiter and converts them into a generic type T
*
* @tparam T - the final type of the data that will be converted from string
* @param s - the input string with tokens to be converted
* @param tokens - the resulting (converted) tokens will be stored here
* @param delim - character to be trimmed off (a space by default)
* @param if_trim_tokens - if true, tokens will be trimmed of white space characters
* @return the reference to the vector of converted data
*/
template<typename T>
std::vector<T> & split(const std::string &s, std::vector<T> &tokens, const std::vector<char> & delim = {' '},
const bool if_trim_tokens = true,const bool if_trim_line = true) {
std::string s_copy(s);
trim(s_copy);
// --- replace all delimiters with the first one
for(int i=1;i<delim.size();++i)
std::replace(s_copy.begin(), s_copy.end(), delim[i], delim[0]);
std::stringstream ss(s_copy);
std::string item;
T data;
while (std::getline(ss, item, delim[0])) {
if (if_trim_tokens) trim(item);
if (item.size() == 0) continue;
if(item.find_first_not_of(' ') != std::string::npos) {
std::stringstream ss2(item);
ss2 >> data;
tokens.push_back(data);
}
}
return tokens;
}
//
// ... ... ...
//
//File: string_utils.cc
//
// ... ... ... ...
//
void split(const std::string &input_str, const std::string &separator, const int max, std::vector<std::string> &results) {
std::string str(input_str);
int i = 0;
size_t found = str.find_first_of(separator);
while (found != std::string::npos) {
if (found > 0) results.push_back(str.substr(0, found));
str = str.substr(found + 1);
found = str.find_first_of(separator);
if (max > -1 && ++i == max) break;
}
if (str.length() > 0) results.push_back(str);
}
template<>
std::vector<std::string> &split<std::string>(const std::string &s,
std::vector<std::string> &tokens,
const std::vector<char> & delim,
const bool if_trim_tokens,const bool if_trim_line) {
std::string s_copy(s);
// --- replace all delimiters with the first one
for(int i=1;i<delim.size();++i)
std::replace(s_copy.begin(), s_copy.end(), delim[i], delim[0]);
if (if_trim_line==true) trim(s_copy);
std::stringstream ss(s_copy);
std::string item;
while (std::getline(ss, item, delim[0])) {
if (if_trim_tokens) trim(item);
if (item.size() == 0) continue;
tokens.push_back(item);
}
return tokens;
}
//
// ... ... ... ...
//
我有两个问题:
如何解决此错误?
为什么此错误在 Linux 中没有显示?
答:
4赞
user17732522
10/20/2023
#1
如果模板是显式专用化的,则在使用显式专用化之前,必须声明显式专用化。否则,程序为 IFNDR(格式错误,无需诊断)。
由于显式专用化定义仅在您显示的一个翻译单元中可见,因此至少在其他翻译单元(包括头文件)中将违反此要求。.cc
您需要在模板定义下方添加显式专用化声明:
template<>
std::vector<std::string> &split<std::string>(const std::string &,
std::vector<std::string>,
const std::vector<char> &,
bool, bool);
它在不同的系统上工作是巧合,这是 IFNDR ODR 类型违规导致的常见行为。事实上,如果它确实有效,那么我怀疑编译器隐式实例化了模板定义,并在某些调用站点上使用它而不是显式专用化。程序的行为可能是不确定的,就像有时使用显式专用化定义或有时使用模板定义一样,具体取决于编译器/链接器的选择。
然而,我不得不说,我不确定为什么存在明确的专业化。似乎很奇怪,具体来说,需要有不同的行为。T = std::string
评论