为什么需要"std::function::operator=(F &&)"来制作临时的"std::function"?

Why `std::function::operator=(F &&)` is required to make a temporary `std::function`?

本文关键字:std function operator 为什么      更新时间:2023-10-16

显然std::function::operator=(F &&f)需要完全按照std::function(std::forward<F>(f)).swap(*this);的行为。

除非我错过了什么,否则这个定义会导致一些多余的移动:

#include <functional>
#include <iostream>
struct A
{
    A() {std::cout << "A()n";}
    A(const A &) {std::cout << "A(const A &)n";}
    A(A &&) {std::cout << "A(A &&)n";}
    A &operator=(const A &) {std::cout << "A &operator=(const A &)n"; return *this;}
    A &operator=(A &&) {std::cout << "A &operator=(A &&)n"; return *this;}
    ~A() {std::cout << "~A()n";}
    void operator()() const {}
};
int main()
{
    std::function<void()> f;
    f = A{};
}

指纹:

A()        // Created by `A{}`
A(A &&)    // Moved into temporary `std::function`, but what's the point?
A(A &&)    // Moved into `f`
~A()       
~A()
~A()

(在GCC 7.2和Clang 3.8上测试(

问题:为什么我们不能通过将复制/移动(取决于值类别(直接复制到 LHS 存储中来消除一次移动?


编辑:我不是在问为什么没有优化这一举动,而是为什么首先要这样做。

为什么有两个动作?

当构造std::function的临时时,被调用的构造函数是

template< class F > 
function( F f );

这是按值传递的,因此第一次移动实际上是移动到此构造函数的参数中,而第二次移动是移动到临时移动。基本上,std::function的典型实现存储指向其可调用目标的指针,因此交换指针足以满足其swap函数,这不会涉及其可调用目标的任何复制/移动。

为什么不直接将 RHS 复制/移动到 LHS 存储中?

由于 LHS 中存储的可调用目标的类型可能与 RHS 的类型不同,因此无法直接执行复制/移动。

swap()为什么参与其中?

这称为复制和交换习惯用法,它表现为具有强异常安全性的赋值。