Явная специализация шаблона - несколько определений
Я сделал явные специализации раньше, я просто не могу понять, почему это не работает:
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 ответа
В дополнение к решению, предоставленному в ответе Брайаном, вы можете объявить специализацию в файле.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
Явная (полная) специализация шаблона функции подчиняется правилу одного определения, поэтому StringUtils::toString<std::string>
не должны быть определены в нескольких единицах перевода. Вы можете решить эту проблему, объявив ее inline
,
Специализация шаблонной функции - почти всегда неправильный ответ.
Классы - это плохие пространства имен.
Просто перегрузите вместо того, чтобы специализироваться.
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
по значению, которое могло бы избежать дополнительного выделения кучи).
Также обратите внимание, что я включил расширение ADL to_string
для пользовательских классов. Просто перегруз to_steing(X)
в X
Пространство имен и StringUtils::toString(X)
находит это.
Ваша непосредственная проблема в том, что вам нужно отметить специализацию inline
,