'&安培;&安培;'在函数参数包中

时间:2018-01-18 17:38:16

标签: c++

我见过多个代码实例,其中函数参数包使用&&符号,如下所示,但我看不出使用这种表示法有什么好处。

return [
    {
        label: '"lodash"',
        kind: monaco.languages.CompletionItemKind.Function,
        documentation: "The Lodash library exported as Node.js modules.",
        insertText: '"lodash": "*"',
        sortText: 'a'
    },
    {
        label: '"express"',
        kind: monaco.languages.CompletionItemKind.Function,
        documentation: "Fast, unopinionated, minimalist web framework",
        insertText: '"express": "*"',
        sortText: 'b'
    },
    {
        label: '"mkdirp"',
        kind: monaco.languages.CompletionItemKind.Function,
        documentation: "Recursively mkdir, like <code>mkdir -p</code>",
        insertText: '"mkdirp": "*"',
        sortText: 'c'
    }
];

我的第一个想法是&amp;&amp; form将专门用于r值对象,但是这个测试证明了错误:

template<typename... Args>
void Function(Args... args)
{
}

template<typename... Args>
void Function(Args&&... args)
{
}

在这里,VS 2017抱怨该功能的两种形式都是可行的候选人。

有人可以解释这两者之间的区别,以及对另一方有什么好处?

3 个答案:

答案 0 :(得分:2)

它们是参数包表单中的转发引用。对于模板参数推导,它们可以匹配任何参数,但模板参数将与普通模板参数相比推导出不同。

转发引用的主要优点是,如果与std::forward一起使用,将保留左值/右值信息。因此,它们被用来“转发”某些东西。

例如,

void real_foo(A const &a);
void real_foo(A &&a);

template<class... Args>
void foo_proxy_ordinary(Args... args) { real_foo(args...); }

template<class... Args>
void foo_proxy_perfect(Args&&... args) { real_foo(std::forward<Args>(args)...); }

普通版本将始终调用real_foo(A const &)版本,因为在foo_proxy内,args始终是左值。

但是,如果传入的参数确实是rvalues,完美版本将选择real_foo(A&&)

将转发引用与参数包相结合,可以轻松编写通用代理函数,而不会在lvalue / rvalue方面造成性能损失。

答案 1 :(得分:2)

T&&

的上下文中使用
template<typename T>
void f(T&& t);

称为 转发参考 ,有时也称为 通用参考

转发参考的主要优势std::forward相结合,可实现所谓的 完美转发 :功能模板将其参数传递给另一个函数(左值为左值,右值为右值)。

现在可以创建 高阶函数 ,将其他函数作为参数或返回它们,或者更高级的函数包装器(例如std::make_shared ),并做其他很酷的事情。

以下是一些材料,它比我可能更好,更详细地解释它:

答案 2 :(得分:1)

  

有人可以解释这两者之间的区别,以及对另一方有什么好处?

参数包的差异与单个参数的差异相同。 Args声明一个“对象参数”(按值传递),Args&&声明一个引用参数(通过引用传递)。

通过引用传递允许人们避免在不需要时复制参数。如果引用是非const的,它还允许修改引用的参数,包括从该对象移动的可能性。

按值传递使调用者明白传递的对象既不会被修改,也不会被称为调用函数的结果。

  

我的第一个想法是&amp;&amp;表单将专门用于r值对象

正如您的测试所示,这确实是一个不正确的假设。当Args是推导类型,即auto或模板参数时,Args&&确实可以是l值引用或r值引用。它取决于Args被推导出来的内容。这简洁地演示了参考折叠规则:

typedef int&  lref;
typedef int&& rref;
int n;
lref&  r1 = n; // type of r1 is int&
lref&& r2 = n; // type of r2 is int&  note this case in particular
rref&  r3 = n; // type of r3 is int&
rref&& r4 = 1; // type of r4 is int&&

使用这样的引用允许转发,即重新绑定到新的左值引用(如果可能)或从对象移动(如果可能)或复制(当前者都不可能时)。

因此,当Args&&是推断类型时,Args被称为转发引用(或通用引用)。