Как специализировать шаблон с параметрами template-tempate

Редактировать в конце

У меня есть функция, которая принимает шаблон:

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)

Но теперь я должен указать все параметры, а не позволить компилятору догадаться обо всем, что ж... это не трагедия.


person PaperBirdMaster    schedule 13.03.2015    source источник
comment
Вы имеете в виду что-то вроде этого ? coliru.stacked-crooked.com/a/1f9dde261c1555e1   -  person uchar    schedule 13.03.2015


Ответы (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);
}

[Живой пример]

person Angew is no longer proud of SO    schedule 13.03.2015
comment
Итак, проблема в том, что версия шаблона-шаблона является частичной специализацией, а версия типа и не-типа — полной специализацией? - person PaperBirdMaster; 13.03.2015
comment
@PaperBirdMaster Довольно много. Я расширил ответ. - person Angew is no longer proud of SO; 13.03.2015
comment
Удивительно @Angew! большое спасибо. Кстати: ссылка на coliru показывает мне пустую страницу, но я уже проверил это, спасибо. - person PaperBirdMaster; 16.03.2015
comment
@PaperBirdMaster Хм, у меня ссылка работает. Во всяком случае, это были всего лишь 4 исходных вызова (из второго фрагмента кода в вашем вопросе), добавленные к коду в ответе. - person Angew is no longer proud of SO; 16.03.2015

Ну, правильный синтаксис для того, что вы пытаетесь сделать, таков:

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";
}

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

person orlp    schedule 13.03.2015
comment
У меня возникают проблемы с вашим исправлением (см. здесь), вариационная структура конфликтует со специализированным. - person PaperBirdMaster; 13.03.2015
comment
@PaperBirdMaster Я удалил пример из своего ответа, но сейчас я не могу писать рабочий пример. - person orlp; 13.03.2015
comment
Извините, что беспокою вас @orlp, я просто пробовал ваше решение, приношу свои извинения. - person PaperBirdMaster; 13.03.2015