模板函数多定义错误 [重复]

Template function multiple definition error [duplicate]

提问人:user366312 提问时间:10/20/2023 更新时间:10/20/2023 访问量:57

问:

在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;
}
//
// ... ... ... ...
//

我有两个问题:

  1. 如何解决此错误?

  2. 为什么此错误在 Linux 中没有显示?

C++ 模板 MSYS2

评论

0赞 user366312 10/20/2023
@RetiredNinja,我不知道。检查原始 git 存储库。
1赞 user17732522 10/20/2023
@RetiredNinja 两者都没有定义。有一个模板定义和一个显式专用化定义。
0赞 jlx 10/20/2023
如果没有必要,您可以删除代码块中的一些注释吗?

答:

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