为什么需要`std :: function :: operator =(F &&)`来创建一个临时的`std :: function`?

Hol*_*Cat 7 c++ std-function

显然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{};
}
Run Code Online (Sandbox Code Playgroud)

打印:

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

(在GCC 7.2和Clang 3.8上测试)

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


编辑:我不是在问为什么移动没有被优化掉,而是为什么它首先被制造出来.

xsk*_*xzr 1

为什么有两个动作?

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

template< class F > 
function( F f );
Run Code Online (Sandbox Code Playgroud)

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

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

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

为何swap()参与?

这称为复制和交换习惯用法,其行为类似于具有强异常安全性的赋值。