模板参数推导
Template argument deduction
我目前正面临一个我自己无法解决的问题。基本上,我试图做的是在C++中实现一些类似 linq 的行为。
我将从标头中的代码开始:
template<typename T, template<class = T> class A,
template<class = T, template<class=T> class = A> class C>
class queryable
{
public:
typedef T value_type;
typedef A<value_type> allocator_type;
typedef C<value_type, allocator_type> container_type; // (1)
typedef queryable<T, A, C> type;
queryable(container_type const &) { }
template<typename _Out> queryable<_Out, A, C> select(/* some delegate */);
// more methods etc
}
这就是我希望它被实例化的方式:
std::vector<int> my_vec;
queryable<std::vector<int> > q(my_vec);
不用说这行不通(否则我不会在这里:)(
现在更奇怪的部分是,即使这样似乎也不起作用:
std::vector<int> my_vec;
queryable<int, std::allocator, std::vector> q(my_vec);
如您所见(通过查看 select 函数(,对我来说重要的是不要只使用这样的东西:
template<typename T> class queryable;
关于如何解决这个问题的任何建议?这可能吗?
任何帮助将不胜感激!
编辑:我得到的错误:
../entry.cpp:19:58: error: type/value mismatch at argument 3 in template parameter list for ‘template<class T, template<class> class A, template<class, template<class> class<template-parameter-2-2> > class C> class failproof::collections::queryable’
../entry.cpp:19:58: error: expected a template of type ‘template<class, template<class> class<template-parameter-2-2> > class C’, got ‘template<class _Tp, class _Alloc> class std::vector’
../entry.cpp:19:61: error: invalid type in declaration before ‘;’ token
编辑2:
据我了解,编译器抱怨 C 没有采用 2 个类参数,而是 1 个类参数和 1 个模板化类参数 (1(,因为我将 C 定义为这样。有没有办法解决这个问题?
有一种通用方法可以"分解"类型以测试它是否由模板创建,并提取传递给该模板的类型。如果需要,也可以访问模板本身并将其他参数传递给它。
vector
是一个类模板。当您对其应用参数时,您会得到类似 vector<int>
的东西,这是一个模板类。模板类是一种特定的类型,就像任何其他类型一样,它恰好是通过类模板创建的。
目标是,给定一个类型T
,测试它是否是模板类,如果是,则访问用于创建它的类模板,并访问传递给类模板的参数。在此示例中,我只测试某些内容是单参数模板还是双参数模板,但该技术可以轻松扩展。
(从技术上讲,vector
是一个双参数模板。第二个参数有一个默认值,所以vector<int>
实际上是vector<int, allocator<int> >
,但它基本上仍然是一个双参数模板,而不是一个参数模板。
最好的起点是我放在 ideone 上的此示例代码。我将在此答案末尾复制爆炸器代码。
我从
typedef list<int> list_of_ints;
并继续使用Exploder
模板访问上述所有信息。例如,Exploder<list_of_ints> :: type_1
是传递给模板的第一个参数,在本例中为 int
。第二个参数(这是默认参数(是allocator<int>
的,可通过 Exploder<list_of_ints> :: type_2
访问。
typedef Exploder<list_of_ints> :: type_2 should_be_an_allocator_int;
给定第二种类型,我们知道它是由模板创建的,我们可以访问它的参数类型, int
,使用 Exploder< should_be_an_allocator_int > :: type_1
,但实际访问allocator
模板并向其传递不同的参数更有趣。在此示例中,下一行的计算结果为 allocator<double>
。
typedef Exploder< should_be_an_allocator_int >
:: rebind<double> :: type should_be_an_allocator_double;
因此,即使您的list<...,...>
类型未使用默认分配器,您也可以访问所使用的分配器,以及用于创建分配器类型的任何类模板。
现在我们有了合适的分配器,我们可以回到原来的模板类list<int>
,int
替换为double
:
Exploder<list_of_ints> :: rebind<double, should_be_an_allocator_double> :: type
为了验证所有这些是否有效,示例代码使用 typeid(...).name()
打印各种对象的实际类型,以及它应该正确的类型。您可以看到它们匹配。
(此外,某些模板的参数不是类型,而是其他类模板,甚至是其他模板模板。应该可以提取所有这些,但我不打算在这里研究。
(最后一个有趣的技术说明。某些类型,例如 allocator
,具有称为rebind
的东西来允许这种访问。但是上面使用的技术适用于所有模板类,即使是那些没有自己rebind
的模板类(
模板爆炸器的完整代码
请参阅我在 ideone 上放置的示例代码以进行完整演示。
template <class>
struct Exploder;
template<class T, template<class> class Template>
struct Exploder< Template<T> > {
static const char * description() { return " One-arg template. Arg 1 is a type "; }
typedef T type_1;
template <class V>
struct rebind {
typedef Template<V> type;
};
};
template<class T, class U, template<class,class> class Template>
struct Exploder< Template<T,U> > {
static const char * description() { return " Two-arg template. All args are types, as opposed to being (unapplied) templates. "; }
typedef T type_1;
typedef U type_2;
template <class V,class W>
struct rebind {
typedef Template<V,W> type;
};
};
template<class S, class T, class U, template<class,class,class> class Template>
struct Exploder< Template<S,T,U> > {
static const char * description() { return " Three-arg template. All args are types, as opposed to being (unapplied) templates. "; }
typedef S type_1;
typedef T type_2;
typedef U type_3;
};
标准容器(分配器(的第二个模板参数是类型,而不是模板,因此您需要将第三个参数更改为类似
template<typename, typename> class C
(请注意,模板参数规范中的默认参数没有任何用途,因此我在此处省略了它们。
然后,您应该能够将模板实例化为
queryable<int, std::allocator, std::vector>
您最好只是对容器类型进行参数化,然后使用其value_type
和allocator_type
定义:
template <typename C> class queryable
{
public:
typedef typename C::value_type value_type;
typedef typename C::allocator_type allocator_type;
typedef C container_type;
};
(一个缺点是不能直接访问分配器模板;但是,如果需要为其他类型的模板实例化分配器类型的嵌套rebind
定义。
此外,typedef iterator const const_iterator;
是错误的;它声明了一个可用于修改序列的不可修改的迭代器,而const_iterator
应该是不能用于修改序列的可修改迭代器。
(关于术语的说明。 vector
是一个类模板,即没有任何参数。vector<int>
是一个模板类,即一个几乎与其他任何类一样的类,除了它是由模板创建的。
可以根据需要使用它,其中 queryable 采用一个模板参数:
queryable< vector<int> > q;
这意味着querable
是一个只有一个参数的模板:
template <typename T>
struct queryable;
然后,使用具有多个参数的专用化:
template <typename ValueType, template<class T,class = allocator<T> > class ContainerTemplate>
struct queryable< ContainerTemplate<Contained> > {
typedef ValueType value_type;
typedef ContainerTemplate<ValueType> container_type;
typedef typename container_type :: allocator_type A;
// typedef ContainerTemplate <WhateverOtherValueTypeYouWish> ...
// typedef A :: rebind <SomeOtherType> ...
};
最后两行(注释掉(显示了如何将ContainerTemplate
用作类模板,并根据需要创建其他类型。 ContainerTemplate
是vector
或list
或set
或类似的东西。
正如@MikeSeymour所指出的,使用 rebind
可能是访问分配器类模板的方法。
关于 ideone 的示例代码
- 如何反转整数参数包
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 如何使用默认参数等选择模板专业化
- 模板参数替换失败,并且未完成隐式转换
- 具有默认模板参数的多态类的模板推导失败
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 函数调用中参数的顺序重要吗
- 部分定义/别名模板模板参数
- 模板-模板参数推导:三个不同的编译器三种不同的行为
- 使用不带参数的函数访问结构元素
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 如何在OMNET++中指定与命令行参数组合的输出文件名
- 如何使用Luacneneneba API正确读取字符串和表参数
- 在派生函数中指定void*参数
- 视图中的参数推导失败:take_while
- static_assert在宏中,但也可以扩展到可以用作函数参数的东西
- 使用指向成员的指针将成员函数作为参数传递
- 没有名称的C++模板参数
- 如何将enable-if与模板参数和参数包一起使用