Определение фактической арности шаблона шаблона аргумента - PullRequest
0 голосов
/ 05 ноября 2018

Я возлюсь с шаблонным метапрограммированием, особенно с последовательностями типов и 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 не был первым, кто жаловался. Я также сомневаюсь, насколько соответствует такая частичная специализация. Это можно обойти? Можно ли определить фактическую арность параметра шаблона и сделать что-то другое на основе этой информации?

1 Ответ

0 голосов
/ 05 ноября 2018

Я думаю, что нашел обходной путь.

Проблема, похоже, связана с псевдонимами типов - они, похоже, напрямую передают любые аргументы шаблона, а не создают экземпляр типа, как в случае с классом или структурой. Мы можем использовать это в нашу пользу, используя структуру в качестве промежуточного шага:

template<template<class, class...> class P, class BIND>
struct bind2nd
{
    template<class... Tn>
    struct impl
    {
        using type = P<Tn...>;
    };
    template<class T1, class... Tn> using type = typename impl<T1, BIND, Tn...>::type;
};

Теперь это работает :). Это немного запутанно, хотя. Интересно, все ли это в соответствии со стандартом, но, похоже, он компилируется на всех основных компиляторах.

Редактировать : Почему я усложняю вещи, используя вложенные типы и псевдонимы? Я могу также использовать деривацию для предикатов:

template<template<class, class...> class P, class BIND>
struct bind2nd
{
    template<class T1, class... Tn>
    struct type : P<T1, BIND, Tn...> { };
};

Чисто и просто. И это делает его почти идентичным первому определению bind2nd в ОП.

...