我目前面临一个我自己无法解决的问题。
基本上我想做的是在 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 定义为这种方式。
有什么办法可以解决这个问题吗?
I'm currently facing a problem I haven't been able to solve myself.
Basically what I'm trying to do is implement some linq-like behaviour in C++.
I'll start off with the code in my header:
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
}
And this is how I'd like it to be instantiated:
std::vector<int> my_vec;
queryable<std::vector<int> > q(my_vec);
Needless to say this doesn't work (otherwist I wouldn't be here :) )
Now the even stranger part is that even this doesn't seem to work:
std::vector<int> my_vec;
queryable<int, std::allocator, std::vector> q(my_vec);
As you can see (by looking at the select function), it is important to me to not just use something like this:
template<typename T> class queryable;
Any suggestions on how to solve this? And is this even possible?
Any help would be appreciated!
EDIT: the errors I'm getting:
../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
EDIT 2:
As far as I understand the compiler is complaining about C not taking 2 class arguments, but 1 class argument and 1 templated class argument (1), because I defined C to be that way.
Is there any way to resolve this issue?
发布评论
评论(3)
有一种通用方法可以“分解”类型来测试它是否是由模板创建的,并提取传递给该模板的类型。如果需要,还可以访问模板本身并向其传递其他参数。
vector
是一个类模板。当您向它应用参数时,您会得到类似vector
的内容,它是一个模板类。模板类是一种特定类型,与任何其他类型一样,它只是恰好是通过类模板创建的。目标是,给定一个类型
T
,测试它是否是一个模板类,如果是,则访问该类模板用于创建它,还用于访问传递给类模板的参数。在此示例中,我只是测试某个东西是一个参数模板还是两个参数模板,但该技术可以轻松扩展。(从技术上讲,
vector
是一个双参数模板。第二个参数有默认值,因此vector
实际上是vector int> >
,但它基本上仍然是一个二参数模板,而不是一个参数模板。)最好的起点是这个 我放在 ideone 上的示例代码。我将在本答案末尾复制 Exploder 代码。
我首先
使用; :: type_1 是传递给模板的第一个参数,在本例中为
Exploder
模板来访问上述所有信息。例如,Exploderint
。第二个参数(这是默认参数)是allocator
,可以通过Exploder 访问。 :: 类型_2
。给定第二种类型(我们知道它是由模板创建的),我们可以使用
Exploder
访问其参数类型
,但更有趣的是实际访问int
。 should_be_an_allocator_int > :: type_1allocator
模板并向其传递不同的参数。在此示例中,下一行的计算结果为allocator
。因此,即使您的
list<...,...>
类型没有使用默认分配器,您也可以访问所使用的分配器,以及任何用于创建分配器类型的类模板。现在我们有了合适的分配器,我们可以回到原来的模板类
list
并将int
替换为double
:为了验证所有这些是否有效,示例代码使用
typeid(...).name()
来打印各种对象的实际类型,以及它所显示的正确类型。应该是。您可以看到它们匹配。(此外,某些模板的参数不是类型,而是其他类模板,甚至其他模板模板。应该可以提取所有这些,但我我不会在这里对此进行深入研究。)
(最后一个有趣的技术说明。某些类型,例如分配器,具有称为重新绑定的东西来允许这种访问。但上面使用的技术适用于所有模板类,即使是那些没有自己的模板类的
rebind
)模板 Exploder 的完整代码
请参阅我放在 ideone 上的示例代码完整的演示。
There is a general method to 'explode' a type to test if it was created by a template, and to extract the types that were passed to that template. It is also possible to access the template itself and pass other parameters to it if you desire.
vector
is a class template. When you apply parameters to it, you get something likevector<int>
, which is a template class. A template class is a specific type, like any other type, it just happens to have been created via a class template.The goal is, given a type
T
, to test if it is a template class, and if so to gain access to the class template that was used to create it, and also to access the parameters that were passed to the class template. In this sample, I just test for whether something is a one-arg or two-arg template, but the technique can easily be extended.(Technically,
vector
is a two-arg template. There is a default for the second parameter, sovector<int>
is actuallyvector<int, allocator<int> >
, but it's still basically a two-arg template, not a one-arg template.)The best place to start is with this sample code I've put on ideone. I'll copy the Exploder code at the end of this answer.
I begin with
and proceed to use the
Exploder
template to access all the above information. For example,Exploder<list_of_ints> :: type_1
is the first parameter that was passed to the template, in this caseint
. The second parameter (this is the defaulted parameter) isallocator<int>
and is accessible withExploder<list_of_ints> :: type_2
.Given this second type, which we know was created by a template, we can access its parameter type,
int
, withExploder< should_be_an_allocator_int > :: type_1
, but it's more interesting to actually access theallocator
template instead and pass a different parameter to it. This next line evaluates, in this example, to anallocator<double>
.So, even if your
list<...,...>
type did not use the default allocator, you can access the allocator that was used, and also any class template that was used to create the allocator type.Now that we have a suitable allocator, we can go back to our original template class
list<int>
and replaceint
withdouble
:To verify all this has worked, the sample code uses
typeid(...).name()
to print the actual type of the various objects, along with the correct type that it should be. You can see that they match.(Also, some templates are such that their parameters are not types, but other class templates, or even other template templates. It should be possible to extract all that, but I'm not going to look into that here.)
(One last interesting technical note. Some types, such as
allocator
, have something calledrebind
to allow this sort of access. But the technique used above works for all template classes, even those without their ownrebind
)The full code for the template Exploder
See sample code I've put on ideone for a full demo.
标准容器(分配器)的第二个模板参数是类型,而不是模板,因此您需要将第三个参数更改为类似的内容
(请注意,默认值模板参数规范中的参数没有任何用途,因此我在这里省略了它们。)
然后您应该能够实例化模板,因为
您最好只对容器类型进行参数化,然后使用其
value_type
和allocator_type
定义:(一个缺点是您无法直接访问分配器模板;但是,您可以使用分配器类型的嵌套如果您需要为其他类型实例化该模板,请重新绑定定义。)
此外,typedef iterator const const_iterator; 是错误的;声明一个不可修改迭代器,可以用于修改序列,而
const_iterator
应该是一个可修改 不能用于修改序列的迭代器。The second template parameter of the standard containers (the allocator) is a type, not a template, so you need to change your third parameter to something like
(Note that the default arguments in your template parameter specifications don't serve any purpose, so I omitted them here.)
Then you should be able to instantiate the template as
You may be better off just parametrising over the container type, and then using its
value_type
andallocator_type
definitions:(One downside is that you can't directly access the allocator template; however, you can use the allocator type's nested
rebind
definition if you need to instantiate that template for other types.)Also,
typedef iterator const const_iterator;
is wrong; that declares an unmodifiable iterator that can be used to modify the sequence, whileconst_iterator
is supposed to be a modifiable iterator that can't be used to modify the sequence.(术语说明。
vector
是一个类模板,即没有任何参数。vector
是一个模板类,即一个类几乎与任何其他类一样,只是它是由模板创建的。)可以按照您的意愿使用它,其中 queryable 采用一个模板参数:
这意味着
querable
是一个只有一个参数的模板:然后您可以使用具有多个参数的专业化:
最后两行(注释掉)显示了如何使用
ContainerTemplate
作为类模板,创建其他类型根据需要。ContainerTemplate
是vector
或list
或set
或类似的东西。正如@MikeSeymour 所指出的,使用
rebind
可能是访问分配器类模板的方法。ideone 上的示例代码
(A note on terminology.
vector
is a class template, i.e. without any parameters. Andvector<int>
is a template class, i.e. a class almost like any other except that it was created by a template.)It is possible to use it as you wish, where queryable takes one template parameter:
This means that
querable
is a template with just one parameter:You then use a specialization that has more than one parameter:
The last two lines, commented out, show how you can use
ContainerTemplate
as a class template, creating other types as required.ContainerTemplate
isvector
orlist
orset
or something like that.As @MikeSeymour has pointed out, the use of
rebind
might be the way to access the allocator class template.Sample code on ideone