Явная специализация шаблона - несколько определений

Я сделал явные специализации раньше, я просто не могу понять, почему это не работает:

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,

Другие вопросы по тегам