简体   繁体   English

为什么不使用通用引用的std :: function模板构造函数?

[英]Why isn't std::function template constructor using universal reference?

I think that constructor with universal reference parameter has better performance than the one without reference. 我认为具有通用引用参数的构造函数比没有引用的构造函数具有更好的性能。

From cppreference ( https://en.cppreference.com/w/cpp/utility/functional/function/function ), we can see that the template constructor for std::function is not using reference. 从cppreference( https://en.cppreference.com/w/cpp/utility/functional/function/function ),我们可以看到std::function的模板构造std::function没有使用引用。

template< class F > function( F f );

Is it a mistake? 这是一个错误吗? If not, why the standard doesn't requires the constructor use universal reference? 如果没有,为什么标准不要求构造函数使用通用引用?

EDIT: 编辑:

Let's think about two cases. 让我们考虑两个案例。

  1. F is a built-in type, here a function pointer whose size is 4 bytes. F是内置类型,这里是一个大小为4个字节的函数指针。

Using universal reference: 使用通用参考:

lvalue case: 4 bytes are copied. 左值情况:复制4个字节。

rvalue case: 4 bytes are copied. rvalue case:复制4个字节。 (Will you apply a std::move to built-in type?) (你std::move应用于内置类型吗?)

Passing by value: 价值传递:

all cases: 4 bytes are copied. 所有情况:复制4个字节。

  1. F is a type with non-trivial copy constructor, ex. F是具有非平凡复制构造函数的类型,例如。 a lambda type which captures a std::string by value. 一个lambda类型,它按值捕获std::string This example stands for most cases, do you agree? 这个例子代表了大多数情况,你同意吗?

Using universal reference: 使用通用参考:

lvalue case: copy constructor is invoked once 左值案例:复制构造函数被调用一次

rvalue case: move constructor is invoked once rvalue case:移动构造函数被调用一次

Passing by value: 价值传递:

lvalue case: a move and a copy 左值案例:移动和复制

rvalue case: a move rvalue案例:一招

Is this classification complete? 这个分类是否完整? If so, we can run into the conclusion that universal reference is no worse then passing by value. 如果是这样的话,我们可以得出这样的结论:普遍的参考并不比通过价值更糟糕。 Then it returns to the original question. 然后它返回到原始问题。

EDIT again: 再次编辑:

Maybe lambdas without captures are the most common cases, where passing by value is actually passing nothing while passing by reference is passing a pointer. 也许lambdas没有捕获是最常见的情况,其中传递值实际上没有传递任何东西,而通过引用传递指针。 This may be the key. 这可能是关键。 LWG 2774 is related to this question. LWG 2774与此问题有关。 See it in comments. 在评论中看到它。

Because that constructor moves its argument, accepting a reference is pointless. 因为该构造函数移动其参数,所以接受引用是没有意义的。 This comes down to the usual advice on when to take values. 这取决于何时采取价值观的通常建议。

If you pass a primitive, like an int , passing by reference is a pessimisation. 如果你传递一个原语,就像一个int ,通过引用传递是一种悲观。 If you pass a complex type, like a std::string , you can already std::move it into the argument (and, if you don't, then that's because you wanted a copy anyway). 如果你传递一个复杂类型,比如std::string ,你可以将std::move到参数中(如果不这样做,那就是因为你还是想要一个副本)。 You get the best of both worlds. 你可以获得两全其美的效果。

// Bog-standard choice between value and ref; original value only observed

void foo(const int& x) { cout << x << '\n'; }
void foo(const int x) { cout << x << '\n'; }  // Better!

void foo(const std::string& x) { cout << x << '\n'; }  // Better!
void foo(const std::string x) { cout << x << '\n'; }


// When we want to store

void storeACopy(int);
void foo(const int& x) { storeACopy(x); }
void foo(const int x) { storeACopy(x); }  // Better!

void storeACopy(std::string);
void foo(const std::string& x) { storeACopy(x); }  // Meh
void foo(std::string x) { storeACopy(std::move(x)); }  // Cheap!


// So, best of both worlds:
template <typename T>
void foo(T x) { storeACopy(std::move(x)); }

// This performs a copy when needed, but allows callsite to
// move instead (i.e. total flexibility) *and* doesn't require silly
// refs-to-primitives

It also signals to the call site that the original object will not be modified unless you specifically yield ownership with std::move . 它还向调用站点发出信号,除非您使用std::move专门获得所有权,否则不会修改原始对象。

If instead the function took a reference, okay you'd potentially save one move if you want to yield. 如果相反该函数接受了引用,那么如果你想要屈服,你可能会保存一个移动。 But, moves are supposed to be super-cheap so we're not concerned about that. 但是,行动应该是超级廉价的,所以我们并不关心这一点。 And if you didn't want to yield then suddenly you have to go through the rigmaroll of an object copy at the callsite. 如果你不想屈服,那么突然你必须通过呼叫现场的对象副本的rigmaroll。 Ugh! 啊!

This pattern is key to making the most out of move semantics. 这种模式是充分利用移动语义的关键。

I think that constructor with universal reference parameter has better performance than the one without reference. 我认为具有通用引用参数的构造函数比没有引用的构造函数具有更好的性能。

Why do you think that? 为什么你这么想? Have you measured? 你测量过吗? What if we're talking about passing int ? 如果我们谈论传递int怎么办? Reference (universal / forwarding or not) is not always a boost for performance. 参考(通用/转发与否)并不总是提升性能。

Is it a mistake? 这是一个错误吗?

No, it's the standard convention to pass function objects by value. 不,它是按值传递函数对象的标准约定。 They are meant to be cheap to copy and when implementing own function objects, you should keep that in mind. 它们意味着复制起来很便宜,在实现自己的功能对象时,你应该牢记这一点。

If not, why the standard doesn't requires the constructor use universal reference? 如果没有,为什么标准不要求构造函数使用通用引用?

Ultimately, why would it? 最终,为什么会这样? :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM