Специализированная функция-член для нешаблонного класса в C ++

Я пытаюсь специализировать функцию-член шаблона нешаблонного класса, используя шаблонный параметр:

#include <array>
class C
{
public:
  template<class Container>
  void Foo( Container& )
  {
    // ...
  }
};

template<class T, std::size_t N>
template<>
void C::Foo< std::tr1::array<T,N> >( std::tr1::array<T,N>& )
{
  // special
}

Я получаю сообщение об ошибке "недопустимое использование явных аргументов шаблона". Каков правильный синтаксис, чтобы сделать это действительным?


Обновление:

Возможно, я запутал проблему чрезмерным упрощением. Что я действительно хочу сделать, так это специально обработать этот случай, когда задействовано зависимое имя, что, как я думаю, может быть причиной того, что здесь мы бросаем гаечный ключ в работу. Моя первоначальная мысль заключалась в том, чтобы перегрузить функцию как таковую:

class C
{
public:
  template<class Iter>
  void Foo( Iter )
  {
    std::cout << "Normal\n";
  }

  template<class T, std::size_t N>
  void Foo( typename std::tr1::array<T,N>::iterator )
  {
    std::cout << "Special\n";
  }
};

int main()
{
  C c;
  std::tr1::array<int,10> a1;
  c.Foo( a1.begin() ); // Doesn't print "Special"!
}

Но специальный Foo не вызывается. Как я могу это сделать?


person metal    schedule 20.05.2011    source источник


Ответы (2)


Шаблонной является только функция-член, а это означает, что вы должны использовать там только один template<...>. Но это тоже не решит проблему, поскольку вы не можете частично специализировать функцию.

Обычный способ решения проблемы - перегрузка, а не специализация (специализация шаблонных функций не так полезна).

struct test {
   template <typename Container>
   void f( Container& ) { ... }

   template <typename T, int N>
   void f( std::array<T,N>& ) { ... }
};

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

РЕДАКТИРОВАТЬ: после обновления

Обновление вопроса полностью меняет проблему. Проблема, которую вы видите, заключается в том, что аргумент второй версии является зависимым именем и, как таковой, не выводится. Учитывая вызов функции, компилятор не может определить, какой тип T и интегральная константа N соответствуют этому конкретному экземпляру. Рассмотрим другой пример:

template <typename T>
struct inner_int {
   typedef int type;
};
template <typename T>
void foo( typename inner_int<T>::type ) {
}
int main() {
   foo( inner_int<double>::type() );
}

Когда компилятор обрабатывает вызов в main, он создает экземпляр шаблона и извлекает тип, из которого он создает временный, а затем пытается решить, что делать с foo, но в это время он знает только, что он вызывается с int rvalue ... исходный inner<double>::type исчез, теперь это просто foo( int() ), и компилятору придется попытаться создать экземпляр inner_int со всеми возможными типами, чтобы определить, подходит ли какой-либо из них, и в худшем случае, как указано выше, многие будут соответствовать .

person David Rodríguez - dribeas    schedule 20.05.2011

Частичная специализация шаблона функции недопустима. Из стандарта C ++ 03, §14 / 2:

объявление-шаблона может появляться только как объявление области пространства имен или класса. В объявлении шаблона функции идентификатор-декларатора должен быть именем-шаблона (т. Е. Не идентификатором-шаблона) < / сильный>. [Примечание: в объявлении шаблона класса, если имя класса - идентификатор-шаблона, объявление объявляет частичную специализацию шаблона класса.]

Вместо этого вы захотите просто перегрузить свою функцию:

class C
{
public:
    template<typename Container>
    void Foo(Container&)
    {
        // ...
    }

    template<typename T, std::size_t N>
    void Foo(std::tr1::array<T, N>&)
    {
        // special
    }
};

ИЗМЕНИТЬ (в ответ на изменение OP):

Если вы ищете обходной путь только для этого конкретного сценария, поскольку std(::tr1)::array<T,N>::iterator просто T*, ваша «особая» перегрузка может быть:

template<typename T>
void Foo(T*&)
{
    // special
}
person ildjarn    schedule 20.05.2011
comment
Повторите правку на T *: в реализации VS2010 итератором является _Array_iterator ‹_Ty, _Size›, который является проверенным итератором (по крайней мере, в режиме отладки). - person metal; 21.05.2011
comment
@mlimber: Верно, проверенные итераторы нарушают этот обходной путь. Однако суть в том (и у меня нет времени объяснять полностью прямо сейчас) то, что вывод аргументов шаблона не работает так, как было бы необходимо для того, чтобы то, что вы хотите сделать, стало возможным; вам нужно будет полностью переосмыслить свой подход, поскольку это был единственный обходной путь, который я мог придумать. - person ildjarn; 21.05.2011
comment
Спасибо. Но не то, что я хотел услышать. - person metal; 21.05.2011