显然std::function::operator=(F &&f)
required to behave与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存储中?
编辑:我不是在问为什么这个举动没有被优化掉,而是为什么它首先被制作出来。
答案 0 :(得分:1)
构造std::function
的临时值时,被调用的构造函数是
template< class F >
function( F f );
是传值,所以第一步实际上是移动到这个构造函数的参数,而第二步是移动到临时。基本上,std::function
stores a pointer到其可调用target的典型实现,因此交换指针就足以支持其swap
函数,该函数不会涉及其任何复制/移动可赎回的目标。
由于LHS中存储的可调用目标的类型可能与RHS的类型不同,因此无法直接执行复制/移动。
swap()
涉及?这称为copy-and-swap idiom,其行为与strong exception safety一致。