Я возлюсь с шаблонным метапрограммированием, особенно с последовательностями типов и STL-подобными алгоритмами, работающими над этими последовательностями. Одна вещь, с которой я столкнулся, была трансформация предикатов, например, связывая один из их параметров
Я думаю, что трудно описать мою проблему без предварительной информации. Вот пример:
#include <type_traits>
// Type sequence
template<class... T>
struct typeseq
{
static constexpr size_t Size = sizeof...(T);
};
// Some algorithm
template<class TS, template<class...> class P>
using remove_if = /* ... some template logic ... */;
// Usage example:
using a = typeseq<int, float, char*, double*>;
using b = remove_if<a, std::is_pointer>; // b is typeseq<int, float>
Как показано здесь, remove_if
требует предиката, который служит оракулом для алгоритма удаления, чтобы определить, какой из элементов удалить. Поскольку мы имеем дело с метапрограммированием, оно имеет форму параметра шаблона шаблона. ( Обратите внимание, что здесь P
использует параметр шаблона variadic. Хотя я ожидаю унарный шаблон, более ранняя версия C ++ имела ограничение, что аргумент шаблона variadic нельзя использовать как не -variadic параметр шаблона, который строго ограничивает преобразования предикатов. ) Требуется, чтобы при создании экземпляра предикат преобразовывался в тип с вложенным членом value
времени компиляции, который можно преобразовать в bool
.
Все хорошо, но, скажем, вы хотите удалить все типы, которые можно преобразовать в int
. Очевидно, std::is_convertible
является двоичным предикатом, но для remove_if
выше требуется унарный предикат. Нам просто нужно исправить второй аргумент шаблона на int
. Давайте определим bind2nd
:
template<template<class, class...> class P, class BIND>
struct bind2nd
{
template<class T1, class... Tn> using type = P<T1, BIND, Tn...>;
};
// and now we should be able to do:
using c = remove_if<ts, bind2nd<std::is_convertible, int>::type>; // c is typeseq<char*, double*>;
К сожалению, это не удается скомпилировать на последних Clang и MSVC ++. Очевидно, проблема заключается в расширении пакета Tn...
до std::is_convertible<T1, BIND, Tn...>
, в то время как std::is_convertible
имеет только 2 параметра шаблона. Кажется, не имеет значения, что на практике пакет пуст ( изолированный пример )
Я бы предпочел не предоставлять «перегрузки» для любой требуемой арности предиката, переданного в bind2nd
. Есть ли способ обнаружить арность P
в bind2nd
выше? GCC позволяет мне частично специализировать его для невариантных версий P
:
template<template<class, class> class P, class BIND>
struct bind2nd<P, BIND>
{
template<class T1> using type = P<T1, BIND>;
};
Но, к сожалению, GCC не был первым, кто жаловался. Я также сомневаюсь, насколько соответствует такая частичная специализация. Это можно обойти? Можно ли определить фактическую арность параметра шаблона и сделать что-то другое на основе этой информации?