Перегрузка шаблона функции

Можно ли обобщить идею перегрузки шаблона функции? Что важно, параметр шаблона или параметр функции? Как насчет возвращаемого значения?

Например, с учетом шаблона функции

template<typename X, typename Y> void func(X x, Y y) {};

какой шаблон перегруженной функции?

1) template<typename X> void func(X x, int y) {};       
2) template<typename X, typename Y> X func(X x, Y y) {};    
3) template<class X, class Y, class Z> void func(X x, Y y, Z z) {};

Ответы

Ответ 1

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

Вы можете использовать два других:

template<typename X> void func(X x, int y);

будет использоваться, если вторым аргументом вызова является int, например func("string", 10);

template<class X, class Y, class Z> void func(X x, Y y, Z z);

будет использоваться, если вы вызываете func с тремя аргументами.


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

14.5.5

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

Не templated (или "менее templated" ) перегрузка предпочтительна для шаблонов, например

template <class T> void foo(T);
void foo(int);

foo(10); //calls void foo(int)
foo(10u); //calls void foo(T) with T = unsigned

Ваша первая перегрузка с одним параметром без шаблона также подпадает под это правило.

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

template <class T> void foo(T);
template <class T> void foo(T*);

int i;
int* p;
int arr[10];

foo(i);  //calls first
foo(p);   //calls second
foo(arr); //calls second: array decays to pointer

Вы можете найти более формальное описание всех правил в той же главе стандарта (шаблоны функций)


И, наконец, есть ситуации, когда две или более перегрузки будут неоднозначными:

template <class T> void foo(T, int);
template <class T> void foo(int, T);

foo(1, 2);

Здесь вызов неоднозначен, потому что оба кандидата одинаково специализированы.

Вы можете устранить такие ситуации с помощью (например) boost::disable_if. Например, мы можем указать, что при T = int, тогда вторая перегрузка не должна включаться в качестве кандидата перегрузки:

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
template <class T>
void foo(T x, int i);

template <class T>
typename boost::disable_if<boost::is_same<int, T> >::type
foo(int i, T x);

foo(1, 2); //calls the first

Здесь библиотека производит "сбой замены" в возвращаемом типе второй перегрузки, если T = int, удаляя ее из набора кандидатов перегрузки.

На практике вам редко приходится сталкиваться с такими ситуациями.

Ответ 2

Здесь есть две отдельные вещи: функция templating и перегрузка функций. Любые две различные объявления шаблонов, вероятно, будут перегрузками друг друга, поэтому ваш вопрос не совсем понятен, как указано. (Три "перегрузки", которые вы даете, не основываются на первом шаблоне, а у вас есть четыре перегрузки для одного и того же имени функции.) Реальная проблема связана с некоторыми перегрузками и вызовом, как вызвать желаемую перегрузку?

Во-первых, тип возврата не участвует в процессе перегрузки независимо от того, существует ли соответствующий шаблон. Итак, # 2 никогда не будет хорошо играть С# 1.

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

  • правила для шаблонов классов более простые и мощные, позволяющие, например, функции рекурсии и (члена), отличающиеся только возвращаемым типом
  • правила для шаблонов функций позволяют компилятору отображать аргументы шаблона из типов аргументов функции

Возможно, вы сможете решить свою проблему с перегрузками шаблонов функций, но у вас может возникнуть проблема с исправлением любой ошибки, которая возникает по мере того, как правила больше и меньше людей знакомы с их сложностями. После нескольких лет взлома шаблона я не знал, что возможно даже тонкое переполнение шаблонов функций. В таких библиотеках, как Boost и GCC STL, альтернативный подход вездесущ. Используйте шаблонный класс оболочки:

template< typename X, typename Y >
struct functor {
    void operator()( X x, Y y );
};
template< typename X > // partial specialization: 
struct functor< X, int > { // alternative to overloading for classes
    void operator()( X x, int y );
};

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

template< typename X, typename Y > void func( X x, Y y ) {
    return functor< X, Y >()( x, y );
}

Мне было бы интересно узнать, может ли перегрузка функций делать что угодно (помимо вычета), что специализация класса [partial] не может...

И тогда, конечно, ваша перегрузка № 3 никогда не столкнется с двусмысленностью, потому что она имеет другое количество аргументов, чем любая другая перегрузка.

Ответ 3

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


Предположим, что шаблон похож на предварительный процессор макроса, который расширяет #defines перед тем, как компилятор увидит их.

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

Вы спрашиваете о типе возврата. Это часть функции "подпись". Две функции с одинаковыми параметрами, но разные типы возврата - это две разные функции.