Понимание специализации шаблона функции-члена C ++ - PullRequest
14 голосов
/ 28 сентября 2011

У меня есть следующий класс:

#pragma once
#include <string>
#include <iostream>

class testclass
{
public:
    template <class T> T item(const std::string& key)
    {
        std::cout << "non-specialized\n";
        return T();
    }
};

Для метода item я хотел бы указать специализацию для строк. Я пытаюсь сделать это следующим образом (в testclass.cpp):

#include "testclass.h"
#include <iostream>

template<> std::string testclass::item(const std::string& key)
{
    std::cout << "specialized\n";
    return std::reverse(key.begin(), key.end());
}

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

#include <iostream>
#include "testclass.h"

int main()
{
    testclass t;
    std::string key = "foo";
    t.item<int>(key);
    std::string s = t.item<std::string>(key);
    std::cout << s << std::endl;
}

Однако на выходе получается

$ ./a.out
non-specialized 
non-specialized
(empty line)

То, что я исключил, было

$ ./a.out
non-specialized 
specialized
oof

Как я могу сделать это правильно? Я использую g ++ 4.5.2 для компиляции программы.

Редактировать

Решением является перемещение всего определения специализации item в testclass.h (но не в класс). У меня были другие ошибки в программе, например, не включая <algorithm> (для реверса), и неправильно думал, что он вернет обратную строку Чтобы добиться исключительного поведения, файл .cpp оставляют пустым, а содержимое заголовка:

#pragma once
#include <string>
#include <iostream>
#include <algorithm>

class testclass
{
    public:
        template <class T> T item(const std::string& key)
        {
            std::cout << "non-specialized\n";
            return T();
        }
};

template<> std::string testclass::item(const std::string& key)
{
    std::cout << "specialized\n";
    std::string s = key;
    std::reverse(s.begin(), s.end());
    return s;
}

Ответы [ 2 ]

11 голосов
/ 28 сентября 2011

Проблема сводится к общей проблеме отсутствия шаблонов в заголовочном файле.При обработке main компилятор не видит специализацию и генерирует собственный экземпляр универсального шаблона для std::string.Это является нарушением ODR, поскольку теперь в одной и той же программе есть две разные специализации для std::string, но компилятор не обязан ее диагностировать.

Простое решение - объявить / определить специализациюв заголовке, чтобы компилятор мог его использовать или, по крайней мере, знать, не генерировать ли специализацию из универсальной версии при обработке main

8 голосов
/ 24 февраля 2013

К сожалению, это не вся история.Явная специализация функции-члена шаблона item должна быть уникальной во всей программе.Если бы вы создали другой модуль перевода, который реализовал бы другую функцию, которая выполняла бы то же самое, что и «main», и связал это с вашей программой, вы бы получили несколько определений.У вас есть пара альтернатив: (1) объявить специализацию встроенной функцией в заголовке (не общим решением);(2) объявить специализацию в заголовке и определить ее в файле '.cpp' (общий ответ).Это прекрасный пример важности понимания на фундаментальном уровне того, как компиляторы и компоновщики реализуют «физическое связывание» различных конструкций C ++.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...