Как специализировать шаблон с параметрами шаблона-шаблона
Редактировать в конце
У меня есть функция, которая принимает шаблон:
template <template <typename ...> class P, typename ... Args>
void f(const P<Args...> &p)
{
std::cout << "Template with " << sizeof...(Args) << " parameters!\n";
}
Он хорошо работает с любыми шаблонами, которые я тестировал до сих пор:
f(std::valarray<int>{}); // Prints: "Template with 1 parameters!"
f(std::pair<char, char>{}); // Prints: "Template with 2 parameters!"
f(std::set<float>{}); // Prints: "Template with 3 parameters!"
f(std::map<int, int>{}); // Prints: "Template with 4 parameters!"
Но, давайте предположим, что я хочу специализировать шаблон для случая, когда он принимает шаблон с двумя параметрами, код ниже не работает:
template <>
void f<template <typename, typename> class P, typename A, typename B>(const P<A, B> &p)
{
std::cout << "Special case!\n";
}
parse error in template argument list
void f<template <typename, typename> class P, typename A, typename B>(const P<Args...> &p) { std::cout << "Template with " << sizeof...(Args) << " parameters!\n"; }
^
'P' does not name a type
void f<template <typename, typename> class P, typename A, typename B>(const P<Args...> &p) { std::cout << "Template with " << sizeof...(Args) << " parameters!\n"; }
^
expected ',' or '...' before '<' token
void f<template <typename, typename> class P, typename A, typename B>(const P<Args...> &p) { std::cout << "Template with " << sizeof...(Args) << " parameters!\n"; }
^
template-id 'f<<expression error> >' for 'void f(int)' does not match any template declaration
void f<template <typename, typename> class P, typename A, typename B>(const P<Args...> &p) { std::cout << "Template with " << sizeof...(Args) << " parameters!\n"; }
AFAIK это довольно просто с другими типами параметров шаблона:
// Type parameter
template <typename TYPE>
void f(TYPE) { std::cout << "Type!\n"; }
// Non-type parameter
template <int VALUE>
void f() { std::cout << "Value " << VALUE << "!\n"; }
// Type parameter specialization
template <>
void f(float) { std::cout << "Special type case!\n"; }
// Non-type parameter specialization
template <>
void f<666>() { static_assert(false, "Forbidden number!"); }
Как мне добиться этой функциональности с помощью шаблонов-шаблонов?
Редактировать:
Как указано в orlp и Angew, шаблоны функций не могут быть частично специализированными, поэтому я должен придерживаться шаблонов объектов, вот моя попытка:
template <template <typename ...> class P, typename ... Args>
struct c
{
using type = P<Args...>;
const std::size_t count = sizeof...(Args);
void f(const type &t)
{
std::cout << "Template with " << sizeof...(Args) << " parameters!\n";
}
};
template <template <typename, typename> class P, typename A, typename B>
struct c<P, A, B>
{
using type = P<A, B>;
void f(const type &t)
{
std::cout << "Spezialized 2 parameters!\n";
}
};
Оно работает:
c<std::valarray, int> c_valarray_int;
c<std::pair, int, char> c_pair_int_char;
c<std::vector, int, std::allocator<int>> c_vector_int;
c<std::map, int, int> c_map_int_int;
c_valarray_int.f({}); // Prints: "Template with 1 parameters!"
c_pair_int_char.f({}); // Prints: "Spezialized with 2 parameters!"
c_vector_int.f({}); // Prints: "Spezialized with 2 parameters!"
c_map_int_int.f({}); // Prints: "Template with 2 parameters!" (expected)
Но теперь я должен указать все параметры вместо того, чтобы компилятор угадывал все это, ну... это не трагедия.
2 ответа
Вы пытаетесь частичной специализации своего шаблона функции - специализации для любого шаблона с двумя параметрами шаблона. Это не разрешено в C++. Шаблоны функций не могут быть частично специализированными.
Посмотрите на это по-другому: явные специализации, которые вы показали для параметров типа tempalte и параметров не типового шаблона, используют конкретные аргументы. Чтобы явно специализировать свой шаблон функции, вы должны сделать что-то вроде этого:
template <>
void f<std::pair, int, double>(const std::pair<int, double> &p)
{
std::cout << "Special case!\n";
}
В вашем оригинальном коде P
, A
а также B
все еще "не связаны" - это параметры, а не аргументы. Явная специализация не может иметь параметров шаблона; все параметры шаблона основного шаблона, являющегося специализированным, должны быть связаны с конкретными аргументами, предоставляемыми этой специализацией.
Чтобы выполнить редактирование: вы все равно можете сохранить интерфейс шаблона функции с выводом аргумента и просто перенаправить вызов (потенциально частично специализированный) шаблон класса, например так:
template <template <typename ...> class P, typename ... Args>
struct c
{
using type = P<Args...>;
static const std::size_t count = sizeof...(Args);
static void f(const type &t)
{
std::cout << "Template with " << sizeof...(Args) << " parameters!\n";
}
};
template <template <typename, typename> class P, typename A, typename B>
struct c<P, A, B>
{
using type = P<A, B>;
static void f(const type &t)
{
std::cout << "Spezialized 2 parameters!\n";
}
};
template <template <class ...> class P, class... Args>
void f(const P<Args...> &t)
{
c<P, Args...>::f(t);
}
Ну, правильный синтаксис для того, что вы пытаетесь сделать, заключается в следующем:
template <template<typename, typename> class P, typename A, typename B>
void f<P, A, B>(const P<A, B> &p) {
std::cout << "Special case!\n";
}
Однако это частичная специализация, которая не допускается для функций. Вы можете превратить свою функцию в класс с помощью статического метода и использовать функцию-оболочку, вызывающую статический метод класса.