我正在尝试使用模板元编程,具体来说是使用类型序列和类似STL的算法来处理这些序列。我遇到的一个问题是转换谓词,例如通过绑定它们的参数。
在提供一些背景信息之前,我认为很难描述我的问题。以下是一个例子:
如下所示,
一切都很好,但是假设您想要删除每种可以转换为
很不幸,这段代码在最新的Clang和MSVC++上无法编译。显然,问题出在将
我不想为传递给
在提供一些背景信息之前,我认为很难描述我的问题。以下是一个例子:
#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个模板参数。实际上,这个可扩展包为空好像并没有关系(见示例)。我不想为传递给
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并不是第一个抱怨的。 我也怀疑这样的部分特化有多符合规范。 有没有办法绕过这个问题? 是否可以检测模板模板参数的实际参数数量,并根据该信息执行不同的操作?