Понимание специализации шаблона члена С++
У меня есть следующий класс:
#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 для компиляции программы.
Edit
Решение - это перемещение всего определения специализации 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;
}
Ответы
Ответ 1
Проблема сводится к общей проблеме отсутствия шаблонов в файле заголовка. Компилятор при обработке main
не видит специализации и генерирует свою собственную экземпляр шаблона generic для std::string
. Это нарушение ODR, так как в этой же программе есть 2 разных специализации для std::string
, но компилятор не требуется для ее диагностики.
Простым решением является объявить/определить специализацию в заголовке, чтобы компилятор мог либо использовать его, либо, по крайней мере, будет знать, что не генерировать специализацию из общей версии при обработке main
Ответ 2
К сожалению, это не вся история. Явная специализация функции "элемент" шаблона-члена должна быть уникальной во всей программе. Если бы вы создали другую систему перевода, которая реализовала другую функцию, которая выполняла ту же функцию, что и "основная", и связала ее с вашей программой, вы получили бы несколько определений. У вас есть несколько альтернатив: (1) объявить специализацию встроенной функцией в заголовке (не общее решение); (2) объявить специализацию в заголовке и определить его в файле .cpp(общий ответ). Это отличный пример важности понимания на фундаментальном уровне того, как компиляторы и компоновщики реализуют "физическое связывание" различных конструкций С++.