Как правильно объявить шаблон, вводя тип функции в качестве параметра (например, std:: function)
Я узнал, что его нетривиально иметь чистый синтаксис, например, в:
std::function<int(float, bool)>
Если я объявляю функцию как:
template <class RetType, class... Args>
class function {};
Это был бы обычный синтаксис для определения функций templated types:
function<int,float,bool> f;
Но он работает со странным трюком с частичной специализированной специализацией
template <class> class function; // #1
template <class RV, class... Args>
class function<RV(Args...)> {} // #2
Почему?
Почему мне нужно предоставить шаблон пустой общей форме с пустым параметром типа (# 1) или иначе он просто не будет компилировать
Ответы
Ответ 1
Это именно то, как был разработан язык. Первичные шаблоны не могут выполнять сложную декомпозицию таких типов; вам нужно использовать частичную специализацию.
Если я правильно понимаю, вы просто хотите написать вторую версию, не указав основной шаблон. Но подумайте о том, как аргументы шаблона сопоставляются с параметрами шаблона:
template <class RV, class Arg1, class... Args>
class function<RV(Arg1, Args...)> {}
function<int(float,bool)>; //1
function<int, float, bool>; //2
Опция 1
- это то, что вы хотите написать, но обратите внимание, что вы передаете один тип функции в шаблон, где параметры являются параметрами двух типов и пакетом параметров типа. Другими словами, запись этого без основного шаблона означает, что ваши аргументы шаблона не обязательно будут соответствовать параметрам шаблона. Опция 2
соответствует параметрам шаблона, но не соответствует специализации.
Это имеет меньшее значение, если у вас есть более чем одна специализация:
template <class RV, class Arg1, class... Args>
class function<RV(Arg1, Args...)> {}
template <class T, class RV, class Arg1, class... Args>
class function<RV (T::*) (Arg1, Args...)> {}
Возможно, вы могли бы придумать некоторые правила для вывода первичного шаблона из специализаций, но мне это кажется довольно ужасным.
Ответ 2
Вы должны иметь в виду, что int (float, bool)
- это тип. Точнее, это функция типа, которая принимает два параметра типа float
и bool
и возвращает int
. "
Поскольку это тип, очевидно, что шаблон должен иметь параметр один в том месте, где вы хотите использовать синтаксис int (float, bool)
.
В то же время наличие только типа функции громоздко. Конечно, вы могли бы сделать это легко. Например, если все, что вам нужно сделать, это какой-то форвардер:
template <class T>
struct CallForwarder
{
std::function<T> forward(std::function<T> f)
{
std::cout << "Forwarding!\n";
return f;
}
};
Однако, как только вы захотите получить доступ к "компонентам" типа функции, вам нужен способ ввода идентификаторов для них. Самый естественный способ сделать это - частичная специализация для типов функций, как и в вашем вопросе (и точно так же, как std::function
).
Ответ 3
Вы действительно можете это сделать:
template <typename T>
class X { };
X<int(char)> x;
И внутри определения X
вы можете создать std::function<T>
, как вы бы создали std::function<int(char)>
. Проблема здесь в том, что вы не можете (по крайней мере легко) получить доступ к типу возврата и типу параметров аргумента (int
и char
здесь).
Используя "трюк", вы можете получить к ним доступ без проблем - "просто" делает ваш код более чистым.
Ответ 4
"Почему мне нужно предоставить шаблон пустой общей форме с пустым параметром типа"
Потому что вы хотите явное различие типов между типами возврата и типами аргументов, с аналогичной ясностью синтаксического сахара. В С++ такой синтаксис не доступен для первой версии template class
. Вы должны специализироваться на этом, чтобы отличить его.
После прочтения вашего Q я просмотрел заголовочный файл <functional>
. Даже std::function
делает тот же трюк:
// <functional> (header file taken from g++ Ubuntu)
template<typename _Signature>
class function;
// ...
/**
* @brief Primary class template for std::function.
* @ingroup functors
*
* Polymorphic function wrapper.
*/
template<typename _Res, typename... _ArgTypes>
class function<_Res(_ArgTypes...)>
Как вы видите в своих комментариях, они рассматривают специализированную версию как "первичный" класс. Следовательно, этот трюк исходит от самого стандартного заголовка!