为了账号安全,请及时绑定邮箱和手机立即绑定

Variadic模板包扩展

Variadic模板包扩展

C++
潇潇雨雨 2019-07-26 14:52:30
Variadic模板包扩展我正在尝试学习可变参数模板和函数。我无法理解为什么这段代码不能编译:template<typename T>static void bar(T t) {}template<typename... Args>static void foo2(Args... args){     (bar(args)...);}int main(){     foo2(1, 2, 3, "3");     return 0;    }当我编译它失败时出现错误:错误C3520:'args':必须在此上下文中扩展参数包(在功能上foo2)。
查看完整描述

3 回答

?
绝地无双

TA贡献1946条经验 获得超4个赞

可以发生包扩展的地方之一是在braced-init-list中。您可以通过将扩展放在虚拟数组的初始化列表中来利用此功能:


template<typename... Args>

static void foo2(Args &&... args)

{

    int dummy[] = { 0, ( (void) bar(std::forward<Args>(args)), 0) ... };

}

要更详细地解释初始化程序的内容:


{ 0, ( (void) bar(std::forward<Args>(args)), 0) ... };

  |       |       |                        |     |

  |       |       |                        |     --- pack expand the whole thing 

  |       |       |                        |   

  |       |       --perfect forwarding     --- comma operator

  |       |

  |       -- cast to void to ensure that regardless of bar()'s return type

  |          the built-in comma operator is used rather than an overloaded one

  |

  ---ensure that the array has at least one element so that we don't try to make an

     illegal 0-length array when args is empty

演示




扩展的一个重要优势{}是它保证了从左到右的评估。


使用C ++ 17 折叠表达式,您只需编写即可


((void) bar(std::forward<Args>(args)), ...);


查看完整回答
反对 回复 2019-07-26
?
达令说

TA贡献1821条经验 获得超6个赞

参数包只能在严格定义的上下文列表中进行扩展,而运算符,不是其中之一。换句话说,不可能使用包扩展来生成由运算符分隔的一系列子表达式组成的表达式,

经验法则是“扩展可以生成一个分隔模式列表,其中,是一个列表分隔符。” 运算符,不构造语法意义上的列表。

要为每个参数调用一个函数,可以使用递归(这是可变参数模板程序员框中的主要工具):

template <typename T>void bar(T t) {}void foo2() {}template <typename Car, typename... Cdr>void foo2(Car car, Cdr... cdr){
  bar(car);
  foo2(cdr...);}int main(){
  foo2 (1, 2, 3, "3");}

实例


查看完整回答
反对 回复 2019-07-26
?
函数式编程

TA贡献1807条经验 获得超9个赞

SHAMELESS COPY [来源批准]

参数包只能在严格定义的上下文列表中进行扩展,而运算符,不是其中之一。换句话说,不可能使用包扩展来生成由运算符分隔的一系列子表达式组成的表达式,

经验法则是“扩展可以生成一个分隔,模式列表,其中,是一个列表分隔符。” 运算符,不构造语法意义上的列表。

要为每个参数调用一个函数,可以使用递归(这是可变参数模板程序员框中的主要工具):

#include <utility>template<typename T>void foo(T &&t){}template<typename Arg0, typename Arg1, typename ... Args>void foo(Arg0 &&arg0, Arg1 &&arg1, Args &&... args){
    foo(std::forward<Arg0>(arg0));
    foo(std::forward<Arg1>(arg1), std::forward<Args>(args)...);}auto main() -> int{
    foo(1, 2, 3, "3");}

有用的非复制信息

你可能在这个答案中没有看到的另一件事是使用说明&&符和std::forward。在C ++中,说明&&符可以表示两种内容之一:rvalue-references或通用引用。

我不会进入rvalue-references,而是进入使用可变参数模板的人; 普遍的参考是神的发送。

完美的转发

std::forward通用引用的一个用途是将类型转换为其他函数。

在你的例子中,如果我们传递int&foo2它,它会自动降级为int因为foo2模板扣除后生成的函数的签名,如果你想arg将它转发给另一个通过引用修改它的函数,你将得到不希望的结果(变量将不会被更改)因为foo2将传递一个引用传递int给它创建的临时变量。为了解决这个问题,我们指定了一个转发函数来对变量(rvalue  lvalue)进行任何类型的引用。然后,为了确保我们传递了我们使用的转发函数中传递的确切类型,那么只有std::forward那么我们是否允许降级类型; 因为我们现在处于最重要的地步。

如果您需要,请阅读有关通用引用完美转发的更多信息 ; Scott Meyers作为一种资源非常棒。


查看完整回答
反对 回复 2019-07-26
  • 3 回答
  • 0 关注
  • 791 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信