Search code examples
c++templateslinker-errorsexplicit-specialization

Explicit template specialization - multiple definitions


I have done explicit specializations before, I just can't figure out why this does not work:

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;
}

The errors I get are linker errors complaining about multiple definitions of the function toString.

Many files in the project use #include "StringUtils.hpp".

How can I try to fix this error? Is something wrong in the class StringUtils?


Solution

  • In addition to the solution provided in the answer by Brian, you can declare the specialization in the .hpp/.tpp file and define it in a .cpp file.

    StringUtils.hpp file:

    #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 file:

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

    Test program:

    #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;
    }
    

    Output of the test program:

    test
    10