Объявление специалиста по членству в типовом классе

Когда я специализирую (статическую) функцию-член/константу в классе шаблона, я смущен относительно того, куда должно идти объявление.

Вот пример того, что мне делать, - прямо из ссылка IBM на специализированную специализацию:

=== Пример специализации для членов IBM ===

template<class T> class X {
public:
   static T v;
   static void f(T);
};

template<class T> T X<T>::v = 0;
template<class T> void X<T>::f(T arg) { v = arg; }

template<> char* X<char*>::v = "Hello";
template<> void X<float>::f(float arg) { v = arg * 2; }

int main() {
   X<char*> a, b;
   X<float> c;
   c.f(10); // X<float>::v now set to 20
}

Вопрос в том, как я могу разделить это на файлы header/cpp? Родовая реализация явно находится в заголовке, но как насчет специализации?

Он не может попасть в заголовочный файл, потому что он конкретный, что приводит к множественному определению. Но если он входит в файл .cpp, это код, который вызывает X:: f(), который знает специализацию, или может полагаться на общий X:: f()?

Пока у меня есть специализация только в .cpp, без объявления в заголовке. У меня нет проблем с компиляцией или даже запуском моего кода (на gcc, не помню версию на данный момент), и она ведет себя так, как ожидалось, - признавая специализацию. Но A) Я не уверен, что это правильно, и я хотел бы знать, что есть, и B) моя документация Doxygen выходит из игры и очень вводит в заблуждение (более того, в <забастовке > момент) вопрос).

То, что кажется самым естественным для меня, было бы чем-то вроде этого, объявив специализацию в заголовке и определяя его в .cpp:

=== XClass.hpp ===

#ifndef XCLASS_HPP
#define XCLASS_HPP

template<class T> class X {
public:
   static T v;
   static void f(T);
};

template<class T> T X<T>::v = 0;
template<class T> void X<T>::f(T arg) { v = arg; }

/* declaration of specialized functions */
template<> char* X<char*>::v;
template<> void X<float>::f(float arg);

#endif

=== XClass.cpp ===

#include <XClass.hpp>

/* concrete implementation of specialized functions */
template<> char* X<char*>::v = "Hello";
template<> void X<float>::f(float arg) { v = arg * 2; }

... но я понятия не имею, правильно ли это. Любые идеи?

Ответ 1

Обычно вы просто определяете специализации inline в заголовке, как dirkgently said.

Вы можете определить специализации в отдельных единицах перевода, но если вас беспокоит время компиляции или раздувание кода:

// x.h:
template<class T> struct X {
    void f() {}
}

// declare specialization X<int>::f() to exist somewhere: 
template<> void X<int>::f();

// translation unit with definition for X<int>::f():
#include "x.h"
template<> void X<int>::f() {
    // ...
}

Итак, ваш подход выглядит отлично. Обратите внимание, что вы можете делать это только с помощью полной специализации, поэтому часто это непрактично.

Подробнее см., например, Часто задаваемые вопросы о шаблонах часовых поясов.

Ответ 2

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

Ответ 3

Чтобы ответить на один из вопросов: is code which calls X::f() aware of the specialization, or might it rely on the generic X::f()?

Если компилятор видит определение, соответствующее его требованиям, он будет использовать его. Иначе он будет генерировать обычные вызовы функций.

В первом фрагменте кода вы предоставляете общее определение для X<T>::f(T arg), поэтому компилятор будет создавать экземпляр для любого T, кроме float.

Если вы должны опустить общее определение, тогда компилятор будет генерировать вызовы, скажем, X<double>::f(double), и компоновщик будет искать определение, которое может закончиться ошибкой компоновщика.

Подводя итог: вы можете иметь все в заголовках, потому что в качестве шаблонов вы не получите несколько определений. Если у вас есть только объявления, вам понадобятся определения в другом месте, чтобы ссылки могли найти позже.