我正在摆弄模板元编程,特别是类型序列和在这些序列上工作的类似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并不是最初抱怨的人。我也怀疑这种局部专业化是如何符合要求的。有没有解决的办法?是否有可能检测到模板模板参数的实际含义,并根据该信息执行其他操作?
答案 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
的第一个定义相同。