我应该始终使用“ T &&”而不是“ const T&”或“ T&”绑定到回调函数吗?

时间:2019-03-22 12:44:14

标签: c++

template <typename T>
void myFunction(..., T && callback) {
    ...
    callback(...);
    ...
}

使用T &&比使用T&const T&更好吗?

甚至可以简单地T按值传递而不是按引用传递。

函数或lambda是否具有左值和右值的概念?我可以std::move使用函数/ lambda吗?

const中的const T&是否强制执行该函数无法修改其闭包?

5 个答案:

答案 0 :(得分:5)

采用转发引用可以有所作为,但是您必须正确调用回调才能看到它。对于函数和lambda,它们是右值还是左值都没有关系,但是如果您有函子,则可以有所作为

template <typename T>
void myFunction(..., T && callback) {
    callback(...);
}

获取转发引用,然后将回调作为左值调用。如果函数对象作为右值传递,并且其调用运算符定义为

,则可能会出错
operator()(...) && { ... }

因为只能在右值上调用。为了使您的函数正常工作,您需要将函数名称包装在std::forward中,以便在与传递给函数的值表达式类别中进行调用。看起来像

template <typename T>
void myFunction(..., T && callback) {
    std::forward<T>(callback)(...); 
    // you should only call this once since callback could move some state into its return value making UB to call it again
}

因此,如果要获取右值和左值,并以右值或左值调用运算符,则应使用上述方法,因为它就像在myFunction的调用站点中进行调用

答案 1 :(得分:1)

  

使用T &&比使用T&或const T&更好吗?

通用引用可以实现完美的转发,因此在通用模板功能中通常更可取。

  

或者甚至简单地用T传递值,而不是引用传递。

当您希望拥有所有权时,这通常是更可取的。

  

函数或lambda是否具有左值和右值的概念?

是的。每个值表达式都有一个值类别。

  

我可以std :: move函数/ lambdas吗?

是的。您几乎可以std::move进行任何操作。

另一件事是是否从std::move移出对象。还有另一个问题是对象是否可以移动。 Lambda是可移动的,除非它们包含不可移动的捕获。

答案 2 :(得分:1)

在OP的示例中:

template <typename T>
void myFunction(..., T && callback) {
    ...
    ...
}

通过T&&将使您两全其美。如果传递一个r值引用,则模板将使用一个r值引用来解析函数,但是如果传递一个l值引用,则该函数将解析为一个l值。传递callback时,请记住要使用std::forward来保留l / r值。

这仅在将T模板化的情况下才有效,而在使用std :: function之类的情况下则不会。

那么我们在非模板示例中要传递什么。首先要确定的是在函数退出后是否可以调用callback。如果仅在myFunc范围内调用回调,则最好使用l值引用,因为您可以直接调用该引用。

但是,如果将在callback的范围之后调用myFunc,则使用r值将允许您move callback。这样可以节省您的副本,但会迫使您保证在传递到myFunc之后,回调不能在其他任何地方使用。

答案 3 :(得分:0)

在C ++函数中(不要与@app.route('/method_a', methods=['GET']) def method_a(): x = request.cookies.get('xcookie', 0) x = x + 1 response = jsonify({'mesaage': 'value of x incremented by 1 is: '+str(x)}) response.set_cookie('xcookie', x) return response 混淆)实际上只是指针。您可以移动指针,尽管它与复制指针相同。

内部的Lambda或多或少是常规的std::function(或struct,它们是相同的),它们实现了class并可能存储某些状态(如果您的Lambda捕获了状态) )。因此operator()的功能与常规结构的功能相同。

类似地,可能会有move个lambda(因为它们的状态中只有可移动的值)。因此,有时候,如果您想接受此类lambda,则需要经过move-only

但是,在某些情况下,您需要一个副本(例如,如果要在线程中执行函数)。

答案 4 :(得分:0)

您可能想要选择T(带有可选的std::ref)或T&&并坚持使用其中一个。

如果您想让呼叫者知道T const&不会修改myFunction,则

callback是有用的。如果callback可能是有状态的,则不起作用。当然,如果将callback的{​​{1}}标记为operator()(或const是非callback lambda),则mutable将不会t修改myFunction。本质上,呼叫callback的人都可以为自己提供这种一致性保证。

如果您希望允许myFunction引入有状态函子,则

T&可以使用。缺点是myFunction无法绑定到右值(例如T&)。

myFunction([&](Type arg){ /* whatever */ })对有状态函子和无状态函子均适用,并且适用于右值(例如lambdas)和左值。如果呼叫T的某人希望在myFunction之外可以观察到callback的状态变化(例如myFunction不仅仅是callback),他们可以使用operator()。这就是C ++标准库所附带的。

std::ref类似地是通用的(它可以处理右值和/或有状态函子),但是它不需要T&&就可以从外部看到对std::ref的更改。 callback