Требуются ли для шаблонов специализации шаблона <> синтаксис?
-
06-09-2019 - |
Вопрос
У меня есть класс посетителей, напоминающий это:
struct Visitor
{
template <typename T>
void operator()(T t)
{
...
}
void operator()(bool b)
{
...
}
};
Четко, operator()(bool b)
предназначен для специализации предыдущей функции шаблона.
Однако у него нет template<>
Синтаксис, который я привык видеть перед ним, объявляя об этом как специализацию шаблона. Но это компилируется.
Это безопасно? Это правильно?
Решение
Ваш код не является специализацией шаблона, а скорее неэлементной функцией. Там есть некоторые различия. Оператор без размера () будет иметь приоритет по сравнению с шаблонной версией (для точного совпадения, но преобразования типа там не будут иметь место), но вы все равно можете вызвать называться шаблонную функцию:
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 <>
синтаксис. Тем не менее, вы должны знать, что эти два подхода, даже если они могут показаться идентичными, тонко разные, и даже компилятор может потеряться при выборе правильной функции для вызова. Перечисление всех возможных случаев было бы слишком долго для этого ответа, но вы можете проверить Херб Саттер ГОТУ #49 по этому вопросу.
О, это будет компилировать. Это просто не будет функцией шаблона. У вас будет регулярная функция, не относящаяся к T-Template, вместо специализации шаблона.
Это безопасно, и на самом деле вероятно, что вы хотите. Образец посетителя обычно реализуется путем перегрузки. Специализирующие шаблоны функций В любом случае, не очень хорошая идея.
То, что вы сделали, это не сериализация шаблона, а перегрузка функций. Это безопасно.
PS Трудно сказать, правильно ли это или нет, не зная, чего вы пытаетесь достичь. Имейте в виду, что независимо от того, что это шаблон или перегруженная функция, ваш оператор будет выбран во время компиляции. Если вам нужно отправить время выполнения, вам нужен полиморфизм, а не перегрузка. Ну, вы, наверное, знаете это все равно; на всякий случай.
У вас есть
void operator()(bool b)
это не шаблонная функцияtemplate< typename T > void operator()(T t)
который является отдельным базовым шаблоном, который перегружает вышеупомянутое
Вы могли бы иметь полную специализацию второго, как в template<> void operator(int i)
что будет рассматриваться только тогда, когда void operator()(bool b)
не совпадал.
Специализация базового шаблона используется для выбора того из методов базового шаблона. Однако в вашем случае у вас есть неэлементный метод, который будет рассмотрен в первую очередь.
Статья Почему бы не специализировать шаблоны функций? дает довольно хорошее объяснение того, как выбран метод.
В Sumary:
- Функции, не являющиеся шаблонами, считаются первыми (это ваш обычный оператор () (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