我目前正面临着一个我自己都无法解决的问题。基本上,我想做的是在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就是这样的。有没有办法解决这个问题?
发布于 2012-01-19 05:40:33
有一个通用的方法来“分解”一个类型,以测试它是否是由模板创建的,并提取传递给该模板的类型。如果需要,还可以访问模板本身并将其他参数传递给它。
vector是一个类模板。当您对其应用参数时,您会得到类似于vector<int>的内容,这是一个模板类。模板类是一种特定的类型,就像任何其他类型一样,它只是碰巧是通过类模板创建的。
在给定类型T的情况下,目标是测试它是否是模板类,如果是,则访问用于创建它的类模板,并访问传递给类模板的参数。在这个示例中,我只是测试某个东西是一个单参数模板还是两个参数模板,但是该技术可以很容易地扩展。
(从技术上讲,vector是两个参数的模板。第二个参数有一个默认值,因此vector<int>实际上是vector<int, allocator<int> >,但它基本上仍然是两个参数的模板,而不是一个参数的模板。)
从这个sample code I've put on ideone开始是最好的选择。我将在这个答案的末尾复制Exploder代码。
我从下面开始
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;对于第二种类型,我们知道它是由一个模板创建的,我们可以用Exploder< should_be_an_allocator_int > :: type_1访问它的参数类型int,但更有趣的是实际访问allocator模板,并向它传递一个不同的参数。在本例中,下一行的计算结果为allocator<double>。
typedef Exploder< should_be_an_allocator_int >
:: rebind<double> :: type should_be_an_allocator_double;因此,即使您的list<...,...>类型没有使用默认分配器,您也可以访问所使用的分配器,以及用于创建该分配器类型的任何类模板。
现在我们有了一个合适的分配器,我们可以返回到最初的模板类list<int>,并用double替换int
Exploder<list_of_ints> :: rebind<double, should_be_an_allocator_double> :: type为了验证所有这些都是有效的,示例代码使用typeid(...).name()来打印各种对象的实际类型,以及它应该是的正确类型。您可以看到它们是匹配的。
(此外,有些模板的参数不是类型,而是其他类模板,甚至是其他模板模板。提取所有这些应该是可能的,但我不会在这里讨论这一点。)
(最后一个有趣的技术说明。有些类型,比如allocator,有一种叫做rebind的东西来允许这种访问。但上面使用的技术适用于所有模板类,甚至是那些没有自己的rebind的模板类)
模板分解程序的完整代码
有关完整演示,请参阅sample code I've put on 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;
};发布于 2012-01-18 21:01:22
标准容器(分配器)的第二个模板参数是一个类型,而不是一个模板,因此您需要将第三个参数更改为类似于
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应该是一个不能用来修改序列的可修改迭代器。
发布于 2012-01-18 21:20:40
(关于术语的注释。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上的示例代码
https://stackoverflow.com/questions/8910045
复制相似问题