检测模板模板参数的实际Arity

时间:2018-11-05 00:54:49

标签: c++ templates c++17 variadic-templates

我正在摆弄模板元编程,特别是类型序列和在这些序列上工作的类似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在这里使用可变参数模板参数。尽管我期望使用一元模板,但是C ++的早期版本有一个限制,即不能使用可变参数模板参数作为非变量模板参数,它严重限制了谓词转换。它要求谓词在实例化时解析为具有可编译的嵌套编译时间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个模板参数。实际上,包装盒是空的并不重要(isolated example

我宁愿不提供传递给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 :(得分:3)

我想我找到了一种解决方法。

问题似乎与类型别名有关-它们似乎直接传递任何模板参数,而不是像使用类或结构一样实例化类型。我们可以通过将struct作为中间步骤来使用它来对我们有利:

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

Now it works :)。虽然有点令人费解。我不知道这是否完全符合标准,但似乎可以在所有主要编译器上进行编译。

编辑:为什么我要使用嵌套类型和别名使事情复杂化?我也可以将推导用于谓词:

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

干净简单。而且它几乎与OP中bind2nd的第一个定义相同。