模板参数演绎

Tom*_*pen 5 c++ linq templates 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
}
Run Code Online (Sandbox Code Playgroud)

这就是我希望它被实例化的方式:

std::vector<int> my_vec;
queryable<std::vector<int> > q(my_vec);
Run Code Online (Sandbox Code Playgroud)

不用说这不起作用(其他我不会在这里:))

现在更奇怪的部分是,即使这似乎不起作用:

std::vector<int> my_vec;
queryable<int, std::allocator, std::vector> q(my_vec);
Run Code Online (Sandbox Code Playgroud)

正如您所看到的(通过查看select函数),对我来说不仅仅是使用这样的东西很重要:

template<typename T> class queryable;
Run Code Online (Sandbox Code Playgroud)

关于如何解决这个问题的任何建议?这甚至可能吗?

任何帮助,将不胜感激!

编辑:我得到的错误:

../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
Run Code Online (Sandbox Code Playgroud)

编辑2:

据我所知,编译器抱怨C不接受2个类参数,而是1个类参数和1个模板化类参数(1),因为我将C定义为那样.有什么方法可以解决这个问题吗?

Aar*_*aid 7

有一种通用方法可以"爆炸"一个类型来测试它是否是由模板创建的,并提取传递给该模板的类型.如果您愿意,也可以访问模板本身并将其他参数传递给它.

vector是一个类模板.当你为它应用参数时,你会得到类似的东西vector<int>,它是一个模板类.一个模板类是特定类型的,像任何其他类型的,它只是碰巧已经通过类模板创建的.

给定一个类型T,目标是测试它是否是模板类,如果是,则访问用于创建它的类模板,以及访问传递给类模板的参数.在这个示例中,我只是测试某个东西是一个arg还是两个arg模板,但是这个技术可以很容易地扩展.

(从技术上讲,它vector是一个双arg模板.第二个参数有一个默认值,vector<int>实际上是vector<int, allocator<int> >,但它基本上仍然是一个双arg模板,而不是一个arg模板.)

最好的起点是这个示例代码,我已经把它放在了ideone上.我会在这个答案的最后复制Exploder代码.

我开始

typedef list<int> list_of_ints;
Run Code Online (Sandbox Code Playgroud)

并继续使用该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;
Run Code Online (Sandbox Code Playgroud)

鉴于这种第二类,我们知道这是由模板创建,我们可以访问它的参数类型,intExploder< 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;
Run Code Online (Sandbox Code Playgroud)

所以,即使你的list<...,...>类型并没有使用默认的分配器,您可以访问已使用的分配器,以及任何类模板是用于创建分配器类型.

现在我们有了一个合适的分配器,我们可以回到我们原来的模板类 list<int>并替换intdouble:

Exploder<list_of_ints> :: rebind<double, should_be_an_allocator_double> :: type
Run Code Online (Sandbox Code Playgroud)

为了验证所有这些是否有效,示例代码用于typeid(...).name()打印各种对象的实际类型以及它应该是正确的类型.你可以看到它们匹配.

(另外,一些模板是这样的,它们的参数不是类型,而是其他类模板,甚至是其他模板模板.应该可以提取所有这些,但我不打算在这里查看.)

(最后一个有趣的技术说明.某些类型,例如allocator,有一些叫做rebind允许这种访问的东西.但上面使用的技术适用于所有模板类,甚至那些没有自己的模板类rebind)

模板Exploder的完整代码

请参阅示例代码我已经将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;
};
Run Code Online (Sandbox Code Playgroud)


Mik*_*our 6

标准容器(分配器)的第二个模板参数是一个类型,而不是模板,因此您需要将第三个参数更改为类似

template<typename, typename> class C
Run Code Online (Sandbox Code Playgroud)

(请注意,模板参数规范中的默认参数不起任何作用,因此我在此省略它们.)

然后你应该能够将模板实例化为

queryable<int, std::allocator, std::vector>
Run Code Online (Sandbox Code Playgroud)

您可能最好只是对容器类型进行参数化,然后使用它value_typeallocator_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;
};
Run Code Online (Sandbox Code Playgroud)

(一个缺点是您无法直接访问分配器模板 ;但是,如果需要为其他类型实例化该模板,则可以使用分配器类型的嵌套rebind定义.)

还有,typedef iterator const const_iterator;错了; 声明了一个可以用来修改序列的不可修改的迭代器,而它应该是一个不可用于修改序列的可修改迭代器.const_iterator