Ответ 1
Сначала перечислим варианты создания нескольких вариантов одного и того же метода шаблонов:
Специализация функции шаблона: НЕ является опцией, поскольку функции шаблона не могут быть частично специализированными. (Смотрите здесь SO-темы на здесь, здесь и здесь).
-
Простая перегрузка: это может работать, , поскольку вопрос упоминает и демонстрирует.
Однако это не всегда хорошо работает, как мы увидим ниже. -
Использование частичной специализации класса функтора: это простая альтернатива для отсутствия специализированной функции шаблона.
-
Использование
std::enable_if
вместе с перегрузкой функций шаблона: этот подход можно выбрать, когда простая перегрузка шаблона не работает, см. ниже.
EDIT: добавление опции @Nir 4
- Использование функциональных параметров, основанных на шаблоне: Этот подход, предложенный Nir в комментариях и представленный ниже, позволяет перегружать функцию шаблона, но требует некоторого громоздкого синтаксиса на стороне вызывающего абонента, см. ниже.
--- КОНЕЦ ИЗМЕНЕНИЯ ---
Возникает вопрос, когда функция перегрузки шаблонов работает нормально, когда параметр шаблона выводится из вызова. Однако в тех случаях, когда вызов функции шаблона предоставляет параметры шаблона напрямую, и необходимо сопоставить реализацию на основе отношений или условий параметров шаблона, перегрузка больше не может помочь.
Рассмотрим следующее:
template <typename T, T val1, T val2>
void isSame1() {
cout << "val1: " << val1 << ", val2: " << val2 << " are "
<< (val1==val2?" ":"NOT ") << "the same" << endl;
}
Хотя val1 и val2 являются KNOWN при компиляции, нет возможности частичной специализации случая, когда мы KNOW во время компиляции, чтобы они были одинаковыми. Перегрузка функций в этом случае не помогает, перегрузка для случая невозможна для двух параметров шаблона непигового типа.
С помощью частичной специализации классов мы можем сделать:
template <typename T, T val1, T val2>
struct IsSameHelper {
static void isSame() {
cout << "val1: " << val1 << ", val2: " << val2 << " are NOT the same" << endl;
}
};
// partial specialization
template <typename T, T val>
struct IsSameHelper<T, val, val> {
static void isSame() {
cout << "val1: " << val << ", val2: " << val << " are the same" << endl;
}
};
template <typename T, T val1, T val2>
void isSame2() {
IsSameHelper<T, val1, val2>::isSame();
}
Или, альтернативно, с std::enable_if
мы можем сделать:
template<typename T, T val1, T val2>
struct is_same_value : std::false_type {};
template<typename T, T val>
struct is_same_value<T, val, val> : std::true_type {};
template <typename T, T val1, T val2>
typename std::enable_if<!is_same_value<T, val1, val2>::value, void>::type isSame3() {
cout << "val1: " << val1 << ", val2: " << val2 << " are NOT the same" << endl;
}
template <typename T, T val1, T val2>
typename std::enable_if<is_same_value<T, val1, val2>::value, void>::type isSame3() {
cout << "val1: " << val1 << ", val2: " << val2 << " are the same" << endl;
}
Основной для всех вышеперечисленных вариантов будет выглядеть так:
int global1 = 3;
int global2 = 3;
//======================================================
// M A I N
//======================================================
int main() {
isSame1<int, 3, 4>();
isSame1<int, 3, 3>();
isSame1<int*, &global1, &global1>();
isSame1<int*, &global1, &global2>();
isSame2<int, 3, 4>();
isSame2<int, 3, 3>();
isSame2<int*, &global1, &global1>();
isSame2<int*, &global1, &global2>();
isSame3<int, 3, 4>();
isSame3<int, 3, 3>();
isSame3<int*, &global1, &global1>();
isSame3<int*, &global1, &global2>();
}
EDIT: добавление опции @Nir 4
template <class T, T v> struct foo{
static constexpr T val = v;
};
// in a .cpp
template <class T, T v>
constexpr T foo<T, v>::val; // required for non-integral / non-enum types
template <class T, T v1, T v2> void isSame4(foo<T, v1> f1, foo<T, v2> f2) {
cout << "val1: " << f1.val << ", val2: " << f2.val << " are NOT the same" << endl;
}
template <class T, T v> void isSame4(foo<T, v> f1, foo<T, v> f2) {
cout << "val1: " << f1.val << ", val2: " << f2.val << " are the same" << endl;
}
Основной для этого параметра будет выглядеть:
int global1 = 3;
int global2 = 3;
//======================================================
// M A I N
//======================================================
int main() {
isSame4(foo<int, 4>(), foo<int, 3>());
isSame4(foo<int, 3>(), foo<int, 3>());
isSame4(foo<int*, &global1>(), foo<int*, &global1>());
isSame4(foo<int*, &global1>(), foo<int*, &global2>());
}
Я не вижу никакого преимущества в синтаксисе 4-го варианта. Но можно думать иначе...
Обратите внимание на необходимость .cpp файла в опции 4, для объявления T foo::val
, во всех остальных вариантах все подходит для файлов .h.
--- КОНЕЦ ИЗМЕНЕНИЯ ---
Подводя итог:
Случаи, когда мы можем получить временное разрешение компиляции, основанное на метапрограмме шаблона, требуется частичная специализация. Это может быть достигнуто для функций через частичную специализацию класса или с помощью enable_if (которая, в свою очередь, нуждается в собственной частичной специализации класса для его условия).
См. код: http://coliru.stacked-crooked.com/a/65891b9a6d89e982