Как связать вложенные шаблонные специализации? - PullRequest
0 голосов
/ 13 июня 2019

Я хотел бы иметь возможность использовать общую функцию разбора строк следующим образом:

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

Следуя предложению здесь , я перенес специализацию шаблона в отдельные файлы *.ipp,которые #include изданы из *.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_

Теперь у меня есть класс со специализированным методом:

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

Если я использую только fromString напрямую, он работает нормально.Однако, если я использую Foo, я получаю ошибки связывания с «множественным определением»:

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

1 Ответ

1 голос
/ 13 июня 2019

В то время как шаблонные функции не требуют inline, чтобы избежать множественных определений, специализация действует как обычные функции.

Вы должны добавить ее:

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