I'm fiddling around with template metaprogramming, specifically with type sequences and STL-like algorithms working on those sequences. One thing I ran into was the transformation of predicates, for example by binding one of their parameters
I think it's hard to describe my problem without first providing some background information. Here's an example:
#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>
As shown here, remove_if requires a predicate that serves as an oracle for the removal algorithm to determine which of the elements to remove. As we're dealing with metaprogramming, it is in the form of a template template parameter. ( Note that P is using a variadic template parameter here. Although I'm expecting a unary template, an earlier version of C++ had the restriction that a variadic template argument can't be used as a non-variadic template parameter, which severely restricts predicate transformations. ) It requires that the predicate, when instantiated, resolves to a type that has a nested compile time value member that is convertible to bool.
All is well, but say you want to remove every type that is convertible to int. Obviously, std::is_convertible is a binary predicate, but remove_if above requires a unary predicate. We just need to fix the second template argument to int. Let's define a 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*>;
Unfortunately, this fails to compile on latest Clang and MSVC++. Apparently, the issue is the pack expansion of Tn... into std::is_convertible<T1, BIND, Tn...> while std::is_convertible only has 2 template parameters. It doesn't seem to matter that the pack is empty in practice (isolated example)
I'd rather not provide 'overloads' for any required arity of the predicate passed into bind2nd. Is there a way to detect the arity of P in bind2nd above? GCC allows me to partially specialize it for non-variadic versions of P:
template<template<class, class> class P, class BIND>
struct bind2nd<P, BIND>
{
template<class T1> using type = P<T1, BIND>;
};
But unfortunately GCC wasn't the one that was complaining in the first place. I also doubt how conformant such a partial specialization is. Is there a way around this? Is it possible to detect the actual arity of a template template parameter, and do something different based on that information?