Sparkler
Sparkler

Reputation: 2913

How to link nested templated specializations?

I would like to be able to use a generic string parsing function as follows:

Utils::parse::fromString<int>(whatever)
Utils::parse::fromString<float>(whatever)

Following the suggestion here, I moved template specialization to separate *.ipp files, which are #includeed from the *.hpp:

Utils.hpp

#ifndef UTILS_HPP_
#define UTILS_HPP_
#include <string>

namespace Utils
{
    namespace parse
    {
        template<typename T>
        T fromString(std::string s);
    }
}

#include "Utils.ipp"
#endif // UTILS_HPP_

Utils.ipp

#ifndef UTILS_IPP_
#define UTILS_IPP_

template<>
int Utils::parse::fromString<int>(std::string s) { return 42; }

template<>
float Utils::parse::fromString<float>(std::string s) { return 42.0; }

#endif // UTILS_IPP_

Now I have a class with a specialized method:

Foo.hpp

#ifndef FOO_HPP_
#define FOO_HPP_

#include <string>
#include "Utils.hpp"

class Foo
{
public:
    Foo();

    template<typename T>
    T get(std::string s);
};

#include "Foo.ipp"

#endif // FOO_HPP_

Foo.ipp

#ifndef FOO_IPP_
#define FOO_IPP_

#include <string>

template<typename T>
T Foo::get(std::string s)
{
    return Utils::parse::fromString<T>(s);
}

#endif // FOO_HPP_

test.cpp

#include <iostream>
#include <string>
#include "Utils.hpp"
#include "Foo.hpp"

// Custom template specialization
template<>
char Utils::parse::fromString<char>(std::string s)
{
    return 'c';
}

int main()
{
    // Calling `fromString` directly - this works!
    const std::string whatever = "whatever";
    std::cout << Utils::parse::fromString<int>(whatever) << std::endl;
    std::cout << Utils::parse::fromString<char>(whatever) << std::endl;

    // Calling `fromString` via `Foo` - linking error!
    Foo foo;
    std::cout << foo.get<int>(whatever) << std::endl;

    return 0;
}

If I only use fromString directly, it works fine. However, if I use Foo, i get "multiple definition" linking errors:

g++ test.cpp Foo.cpp
/tmp/cc7ACcVe.o: In function `int Utils::parse::fromString<int>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
Foo.cpp:(.text+0x0): multiple definition of `int Utils::parse::fromString<int>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
/tmp/ccrN171X.o:test.cpp:(.text+0x0): first defined here
/tmp/cc7ACcVe.o: In function `float Utils::parse::fromString<float>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
Foo.cpp:(.text+0xf): multiple definition of `float Utils::parse::fromString<float>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
/tmp/ccrN171X.o:test.cpp:(.text+0xf): first defined here
collect2: error: ld returned 1 exit status

Upvotes: 0

Views: 29

Answers (1)

Jarod42
Jarod42

Reputation: 217478

Whereas template functions doesn't require inline to avoid multiple definitions, specialization does as regular functions.

You have to add it:

template <>
inline int Utils::parse::fromString<int>(std::string s) { return 42; }

template<>
inline float Utils::parse::fromString<float>(std::string s) { return 42.0; }

Upvotes: 1

Related Questions