Требуют ли специализации шаблона синтаксис шаблона <>?
У меня есть класс посетителя, похожий на этот:
struct Visitor
{
template <typename T>
void operator()(T t)
{
...
}
void operator()(bool b)
{
...
}
};
Очевидно, что operator()(bool b)
предназначен для специализации предыдущей функции шаблона.
Тем не менее, он не имеет template<>
синтаксис, который я привык видеть до этого, объявляя это как специализацию шаблона. Но это компилируется.
Это безопасно? Это правильно?
5 ответов
Ваш код - это не специализация шаблона, а скорее не шаблонная функция. Там есть некоторые отличия. Оператор без шаблонов () будет иметь приоритет над шаблонной версией (для точного соответствия, но преобразования типов там не будут выполняться), но вы все равно можете принудительно вызывать шаблонную функцию:
class Visitor
{
public: // corrected as pointed by stefanB, thanks
template <typename T>
void operator()( T data ) {
std::cout << "generic template" << std::endl;
}
void operator()( bool data ) {
std::cout << "regular member function" << std::endl;
}
};
template <> // Corrected: specialization is a new definition, not a declaration, thanks again stefanB
void Visitor::operator()( int data ) {
std::cout << "specialization" << std::endl;
}
int main()
{
Visitor v;
v( 5 ); // specialization
v( true ); // regular member function
v.operator()<bool>( true ); // generic template even if there is a non-templated overload
// operator() must be specified there (signature of the method) for the compiler to
// detect what part is a template. You cannot use <> right after a variable name
}
В вашем коде нет большой разницы, но если ваш код должен передать тип параметра шаблона, он станет смешнее:
template <typename T>
T g() {
return T();
}
template <>
int g() {
return 0;
}
int g() {
return 1;
}
int main()
{
g<double>(); // return 0.0
g<int>(); // return 0
g(); // return 1 -- non-templated functions take precedence over templated ones
}
Здесь у вас есть перегрузка функций; чтобы получить специализацию шаблона, вам действительно нужно template <>
синтаксис. Однако вы должны знать, что эти два подхода, даже если они кажутся идентичными, слегка различаются, и даже компилятор может потеряться при выборе правильной функции для вызова. Перечисление всех возможных случаев было бы слишком длинным для этого ответа, но вы можете проверить Herb Sutter GoTW #49 на эту тему.
О, это скомпилируется. Это просто не будет функцией шаблона. У вас будет обычная не шаблонная функция вместо шаблонной специализации.
Это безопасно, и вполне вероятно, что вы хотите, а также. Шаблон Visitor обычно реализуется путем перегрузки. В любом случае, специализировать шаблоны функций не очень хорошая идея.
То, что вы сделали, это не сериализация шаблонов, а перегрузка функций. Это безопасно.
PS Трудно сказать, правильно это или нет, не зная, чего вы пытаетесь достичь. Имейте в виду, что независимо от того, является ли это шаблоном или перегруженной функцией, ваш оператор будет выбран во время компиляции. Если вам нужна диспетчеризация во время выполнения, вам нужен полиморфизм, а не перегрузка. Ну, вы, наверное, все равно это знаете; так, на всякий случай.
У тебя есть
void operator()(bool b)
это не шаблонная функцияtemplate< typename T > void operator()(T t)
который является отдельным базовым шаблоном, который перегружает вышеуказанное
Вы можете иметь полную специализацию второго, как в template<> void operator(int i)
который будет рассматриваться только тогда, когда void operator()(bool b)
не соответствует.
Специализация базового шаблона используется для выбора методов базового шаблона для вызова. Однако в вашем случае у вас есть не шаблонный метод, который будет рассмотрен в первую очередь.
В статье Почему не специализируются шаблоны функций? дает довольно хорошее объяснение того, как метод выбран.
В общем:
- Не шаблонные функции рассматриваются в первую очередь (это ваш простой оператор ()(bool) выше)
- Вторые проверяются базовые шаблоны функций (это ваша шаблонная функция), выбирается наиболее специализированный базовый шаблон, а затем, если у него есть специализация для конкретных типов, то используется специализация, в противном случае базовый шаблон используется с "правильными" типами (см. объяснение в статье)
Пример:
#include <iostream>
using namespace std;
struct doh
{
void operator()(bool b)
{
cout << "operator()(bool b)" << endl;
}
template< typename T > void operator()(T t)
{
cout << "template <typename T> void operator()(T t)" << endl;
}
};
// note can't specialize inline, have to declare outside of the class body
template<> void doh::operator()<>(int i)
{
cout << "template <> void operator()<>(int i)" << endl;
}
template<> void doh::operator()<>(bool b)
{
cout << "template <> void operator()<>(bool b)" << endl;
}
int main()
{
doh d;
int i;
bool b;
d(b);
d(i);
}
Вы получаете звонки на:
operator()(bool b) <-- first non template method that matches
template <> void operator()(int i) <-- the most specialized specialization of templated function is called