首页 > 解决方案 > 为什么 std::function 模板构造函数不使用通用引用?

问题描述

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

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

template< class F > function( F f );

这是一个错误吗?如果不是,为什么标准不要求构造函数使用通用引用?

编辑:

让我们考虑两种情况。

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

使用通用参考:

左值案例:复制 4 个字节。

右值情况:复制 4 个字节。(您会将 astd::move应用于内置类型吗?)

按值传递:

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

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

使用通用参考:

左值案例:复制构造函数被调用一次

右值案例:移动构造函数被调用一次

按值传递:

左值案例:移动和复制

右值案例:移动

这个分类是否完整?如果是这样,我们可以得出结论,通用引用并不比按值传递更糟糕。然后回到原来的问题。

再次编辑:

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

标签: c++c++11

解决方案


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

如果你传递一个原语,比如一个int,通过引用传递是一种悲观。如果你传递一个复杂的类型,比如 a 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.

如果该函数取而代之的是引用,那么如果您想屈服,那么您可能会节省一步。但是,移动应该是超级便宜的,所以我们并不担心。如果你不想让步,那么突然间你必须在调用点检查对象副本的复杂性。啊!

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


推荐阅读