草庐IT

c++ - 模板参数推导

coder 2024-02-07 原文

我目前面临一个我自己无法解决的问题。 基本上我想做的是在 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> >,但它基本上仍然是双参数模板,而不是单参数模板。)

最好的起点是这个 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;

给定第二种类型,我们知道它是由模板创建的,我们可以使用 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 )

模板Exploder的完整代码

完整演示请参见 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;
};

关于c++ - 模板参数推导,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8910045/

有关c++ - 模板参数推导的更多相关文章

  1. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  4. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  5. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

  6. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  7. ruby-on-rails - 在默认方法参数中使用 .reverse_merge 或 .merge - 2

    两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option

  8. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  9. ruby - rails 3 redirect_to 将参数传递给命名路由 - 2

    我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use

  10. ruby - 字符串文字中的转义状态作为 `String#tr` 的参数 - 2

    对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一

随机推荐