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

时间:2018-03-07 14:39:55

标签: c++ std-function

显然std::function::operator=(F &&f) required to behavestd::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存储中?

编辑:我不是在问为什么这个举动没有被优化掉,而是为什么它首先被制作出来。

1 个答案:

答案 0 :(得分:1)

为什么有两个动作?

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

template< class F > 
function( F f );

是传值,所以第一步实际上是移动到这个构造函数的参数,而第二步是移动到临时。基本上,std::function stores a pointer到其可调用target的典型实现,因此交换指针就足以支持其swap函数,该函数不会涉及其任何复制/移动可赎回的目标。

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

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

为什么swap()涉及?

这称为copy-and-swap idiom,其行为与strong exception safety一致。