Требуют ли специализации шаблона синтаксис шаблона <>? - PullRequest
8 голосов
/ 02 июня 2009

У меня есть класс посетителя, похожий на этот:

struct Visitor 
{
    template <typename T>
    void operator()(T t)
    {
        ...
    }

    void operator()(bool b)
    {
        ...
    }
};

Понятно, что operator()(bool b) предназначен для специализации предыдущей функции шаблона.

Однако, он не имеет синтаксиса template<>, который я привык видеть до этого, объявляя это специализацией шаблона. Но он компилируется.

Это безопасно? Это правильно?

Ответы [ 5 ]

19 голосов
/ 02 июня 2009

Ваш код - не шаблонная специализация, а скорее не шаблонная функция. Там есть некоторые отличия. Оператор без шаблонов () будет иметь приоритет над шаблонной версией (для точного соответствия, но преобразования типов там не будут выполняться), но вы все равно можете принудительно вызывать шаблонную функцию:

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
}
5 голосов
/ 02 июня 2009

То, что у вас есть, это перегрузка функции; чтобы получить специализацию шаблона, вам действительно нужен синтаксис template <>. Однако вы должны знать, что эти два подхода, даже если они могут показаться идентичными, слегка различаются, и даже компилятор может потеряться при выборе правильной функции для вызова. Перечисление всех возможных случаев будет слишком длинным для этого ответа, но вы можете проверить Herb Sutter GoTW # 49 на эту тему.

4 голосов
/ 02 июня 2009

О, это скомпилируется. Это просто не будет функцией шаблона. У вас будет обычная не шаблонная функция вместо шаблонной специализации.

Это безопасно, и вполне вероятно, что вы тоже хотите. Шаблон Visitor обычно реализуется путем перегрузки. Специализировать шаблоны функций не очень хорошая идея.

2 голосов
/ 02 июня 2009

У вас есть

  • void operator()(bool b) это не шаблонная функция
  • template< typename T > void operator()(T t), который является отдельным базовый шаблон, который перегружает выше

Вы можете иметь полную специализацию второй, как в template<> void operator(int i), которая будет рассматриваться только тогда, когда void operator()(bool b) не совпадает.

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

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

В общем:

  1. Не шаблонные функции считается первым (это ваша равнина оператор () (bool) выше)
  2. Проверяются базовые шаблоны функций второй (это ваш шаблон функция), выбирается наиболее специализированный базовый шаблон, а затем, если у него есть специализация для конкретных типов, используется специализация, в противном случае базовый шаблон используется с «правильными» типами (см. пояснение в статье)
* 1 028 * Пример:
#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
2 голосов
/ 02 июня 2009

То, что вы сделали, это не сериализация шаблона, а перегрузка функции. Это безопасно.

P.S. Трудно сказать, правильно это или нет, не зная, чего вы пытаетесь достичь. Имейте в виду, что независимо от того, является ли это шаблоном или перегруженной функцией, ваш оператор будет выбран во время компиляции. Если вам нужна диспетчеризация во время выполнения, вам нужен полиморфизм, а не перегрузка. Ну, вы, наверное, все равно это знаете; на всякий случай.

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