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

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

本文关键字:引用 std 函数模板 构造函数 为什么      更新时间:2023-10-16

我认为具有通用引用参数的构造函数比没有引用的构造函数具有更好的性能。

从 cppreference (https://en.cppreference.com/w/cpp/utility/functional/function/function),我们可以看到std::function的模板构造函数没有使用引用。

template< class F > function( F f );

是不是搞错了?如果不是,为什么标准不要求构造函数使用通用引用?

编辑:

让我们考虑两种情况。

  1. F 是内置类型,此处为大小为 4 个字节的函数指针。

使用通用引用:

左值大小写:复制 4 个字节。

右值大小写:复制 4 个字节。(是否会将std::move应用于内置类型?

按值传递:

所有情况:复制 4 个字节。

  1. F 是一种具有非平凡复制构造函数的类型,例如按值捕获std::string的 lambda 类型。这个例子代表大多数情况,你同意吗?

使用通用引用:

左值大小写:复制构造函数被调用一次

右值大小写:移动构造函数被调用一次

按值传递:

左值案例:移动和复制

右值案例:移动

此分类是否完整?如果是这样,我们可以得出这样的结论:普遍引用并不比按价值传递更差。然后它回到原来的问题。

再次编辑:

也许没有捕获的 lambda 是最常见的情况,其中按值传递实际上不传递任何内容,而按引用传递传递是传递指针。这可能是关键。LWG 2774 与这个问题有关。在评论中看到它。

因为构造函数会移动它的参数,所以接受引用是没有意义的。这归结为关于何时接受价值观的通常建议。

如果你传递一个基元,比如int,通过引用传递是一种悲观。如果你传递一个复杂类型,比如std::string,你已经可以把它std::move到参数中(如果你不这样做,那是因为你无论如何都想要一个副本)。您可以两全其美。

// 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

它还向调用站点发出信号,除非您明确放弃std::move所有权,否则不会修改原始对象。

相反,如果函数采用引用,好吧,如果您想屈服,您可能会保存一个移动。但是,搬家应该是超级便宜的,所以我们不担心这一点。如果你不想屈服,那么突然间你必须在调用站点上经历对象副本的僵化。呸!

此模式是充分利用移动语义的关键。

我认为具有通用引用参数的构造函数比没有引用的构造函数具有更好的性能。

你为什么这么认为?你测量过吗?如果我们谈论的是传递int怎么办?参考(通用/转发与否)并不总是性能提升。

是不是搞错了?

不,这是按值传递函数对象的标准约定。它们旨在复制成本低廉,在实现自己的函数对象时,您应该牢记这一点。

如果不是,为什么标准不要求构造函数使用通用引用?

最终,为什么会这样?:)