显式模板专业化-多个定义

时间:2018-12-06 22:24:59

标签: c++ templates linker-errors explicit-specialization

我以前做过显式专业化,我只是想不通为什么它不起作用:

StringUtils.hpp

#ifndef AYC_STRINGUTILS_HPP
#define AYC_STRINGUTILS_HPP

#include <string>

class StringUtils {
public:
    template <typename T>
    static std::string toString(const T& t);
};

#include "StringUtils.tpp"

#endif //AYC_STRINGUTILS_HPP

StringUtils.tpp

#include "StringUtils.hpp"

template<typename T>
std::string StringUtils::toString(const T& t) {
    return std::to_string(t);
}

template<>
std::string StringUtils::toString<std::string>(const std::string& t) {
    return t;
}

我得到的错误是链接器错误,抱怨函数toString的多个定义。

项目中的许多文件都使用#include "StringUtils.hpp"

如何解决此错误? StringUtils类有问题吗?

3 个答案:

答案 0 :(得分:2)

功能模板的显式(完全)专业化必须遵循一个定义规则,因此不得StringUtils::toString<std::string>定义为多个转换单元。您可以通过声明为inline来解决此问题。

答案 1 :(得分:2)

除了answer by Brian中提供的解决方案之外,您还可以在.hpp / .tpp文件中声明特殊化,并在.cpp文件中进行定义。

StringUtils.hpp文件:

#ifndef AYC_STRINGUTILS_HPP
#define AYC_STRINGUTILS_HPP

#include <string>

class StringUtils {
public:
    template <typename T>
    static std::string toString(const T& t);
};

// Generic implementation.
template<typename T>
std::string StringUtils::toString(const T& t) {
    return std::to_string(t);
}

// Declation of the specialization.
template<>
std::string StringUtils::toString<std::string>(const std::string& t);

#endif //AYC_STRINGUTILS_HPP

StringUtils.cpp文件:

#include "StringUtils.hpp"

// Definition of the specialization.
template<>
std::string StringUtils::toString<std::string>(const std::string& t) {
    return t;
}

测试程序:

#include <iostream>
#include "StringUtils.hpp"

int main()
{
   std::string a("test");
   std::cout << StringUtils::toString(a) << std::endl;
   std::cout << StringUtils::toString(10) << std::endl;
}

测试程序的输出:

test
10

答案 2 :(得分:0)

模板功能专业化几乎总是错误的答案。

类是不良的名称空间。

简单地重载而不是专门化。

namespace StringUtils {
  template <typename T>
  std::string toString(const T& t){
    using std::to_string;
    return to_string(t);
  }
  inline std::string toString(std::string s){ return std::move(s); }
}

超载分辨率可以满足您的要求,并且可以实现有效的签名变化(如上述,我按s取值,可以避免额外的堆分配)。

还要注意,我为自定义类启用了to_string的ADL扩展。只需在to_steing(X)的名称空间中重载X,然后StringUtils::toString(X)即可找到它。


您的直接问题是您需要标记专业化inline