【问题标题】:Detecting actual arity of template template argument检测模板模板参数的实际数量
【发布时间】:2019-04-08 09:01:38
【问题描述】:

我正在摆弄模板元编程,特别是类型序列和处理这些序列的类似 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&lt;T1, BIND, Tn...&gt; 的包扩展,而 std::is_convertible 只有 2 个模板参数。在实践中包是空的似乎并不重要(isolated example

我宁愿不为传递给bind2nd 的谓词的任何必需数量提供“重载”。有没有办法在上面的bind2nd 中检测P 的数量? GCC 允许我为P 的非可变版本部分专门化它:

template<template<class, class> class P, class BIND>
struct bind2nd<P, BIND>
{
    template<class T1> using type = P<T1, BIND>;
};

但不幸的是,首先抱怨的不是 GCC。我也怀疑这种部分专业化的一致性。有没有解决的办法?是否可以检测模板模板参数的实际数量,并根据该信息做一些不同的事情?

【问题讨论】:

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


    【解决方案1】:

    我想我找到了解决方法。

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

    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 的第一个定义相同。

    【讨论】:

    • @bolov 谢谢,这确实解释了很多。另外,不知道为什么我如此热衷于使用别名,而我也可以使用基类。我已经扩展了我的答案。
    猜你喜欢
    • 1970-01-01
    • 2018-12-03
    • 1970-01-01
    • 1970-01-01
    • 2019-11-22
    • 1970-01-01
    • 1970-01-01
    • 2010-09-18
    • 1970-01-01
    相关资源
    最近更新 更多