【问题标题】:Why `std::function::operator=(F &&)` is required to make a temporary `std::function`?为什么需要 `std::function::operator=(F &&)` 来创建临时 `std::function`?
【发布时间】:2018-03-07 14:39:54
【问题描述】:

显然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 存储中来消除一个移动?


编辑:我不是在问为什么这个动作没有被优化掉,而是为什么它一开始就被做了。

【问题讨论】:

  • 您是否质疑为什么需要额外的移动或者为什么它没有被优化移除?
  • @Slava 我更感兴趣的是为什么需要额外的移动。
  • 另外,我想知道为什么说 move 不使用A 重用堆分配的缓冲区?
  • 我认为你应该修改你的问题以明确这一点。

标签: c++ std-function


【解决方案1】:

为什么有两个动作?

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

template< class F > 
function( F f );

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

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

由于LHS中存储的callable target的类型可能与RHS不同,所以不能直接进行复制/移动。

swap() 为何参与其中?

这称为copy-and-swap idiom,其行为与strong exception safety 的赋值一样。

【讨论】:

  • 但是可以复制/移动到新分配的对象中,如果没有抛出,删除旧对象并替换指针。除了需要更多的代码(无论如何标准库开发人员不应该关心)之外,这是否比复制和交换更糟糕?
  • 另外,你对两个动作的解释在我看来并不正确。 std::function&lt;void()&gt; f(A{}); 产生一次移动,这显然意味着编译器足够聪明,可以省略移动到参数并直接移动到内部缓冲区。所以第一步是进入临时的,第二步似乎是因为交换。
  • @HolyBlackCat 对于你的第一个问题,我认为它需要更多的词来描述规范的行为,所以它更糟(至少没有更好)。对于您的第二个问题,编译器可以忽略来自A{} 的移动,但它不能忽略来自std::forward&lt;A&gt;(f) 的移动,因为f 是一个已经存在的对象,而不是临时对象。可以直接调用swap看证据,比如this,看看有没有动静。
  • @HolyBlackCat 除了您的第一个问题,请注意我们正在讨论 cppreference (或标准)的规范,它并不妨碍真正的实现在 as-if 规则下这样做。
猜你喜欢
  • 2017-07-25
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 2016-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-18
相关资源
最近更新 更多