gpt4 book ai didi

c++ - 为什么 make_unique 有一个可以将 std::bind 作为参数的构造函数的额外移动?

转载 作者:可可西里 更新时间:2023-11-01 17:40:17 24 4
gpt4 key购买 nike

我有一个简单的类,它的构造函数如下所示:

Event(std::function<void()> &&f) : m_f(std::move(f)) { }

构造函数可以与 std::bind 一起使用:

Thing thing;
std::unique_ptr<Event> ev(new Event(std::bind(some_func,thing)));

以上述方式使用它会导致“事物”的一个拷贝构造,然后在该拷贝上进行移动构造。

但是,执行以下操作:

std::unique_ptr<Event> ev = make_unique<Event>(std::bind(some_func,thing));

导致两个移动结构。我的问题是:

  • 什么时候调用“thing”的移动构造函数
  • 为什么用 make_unique 调用了两次?

这是最小的例子:

#include <iostream>
#include <memory>
#include <functional>
using namespace std;

class Thing
{
public:
Thing() : x(0)
{

}

Thing(Thing const &other)
{
this->x = other.x;
std::cout << "Copy constructed Thing!\n";
}

Thing(Thing &&other)
{
this->x = other.x;
std::cout << "Move constructed Thing!\n";
}

Thing & operator = (Thing const &other)
{
this->x = other.x;
std::cout << "Copied Thing!\n";
return (*this);
}

Thing & operator = (Thing && other)
{
this->x = other.x;
std::cout << "Moved Thing!\n";
return (*this);
}

int x;
};

class Event
{
public:
Event() { }
Event(function<void()> && f) : m_f(std::move(f)) { }
void SetF(function<void()> && f) { m_f = std::move(f); }

private:
function<void()> m_f;
};

int main() {

auto lambda = [](Thing &thing) { std::cout << thing.x << "\n"; };

Thing thing;
std::cout << "without unique_ptr: \n";
Event ev(std::bind(lambda,thing));
std::cout << "\n";

std::cout << "with unique_ptr, no make_unique\n";
unique_ptr<Event> ev_p(new Event(std::bind(lambda,thing)));
std::cout << "\n";

std::cout << "with make_unique: \n";
auto ev_ptr = make_unique<Event>(std::bind(lambda,thing));
std::cout << "\n";

std::cout << "with SetF: \n";
ev_ptr.reset(nullptr);
ev_ptr = make_unique<Event>();
ev_ptr->SetF(std::bind(lambda,thing));
std::cout << "\n";

return 0;
}

输出:

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
or
clang++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out

without unique_ptr:
Copy constructed Thing!
Move constructed Thing!

with unique_ptr, no make_unique
Copy constructed Thing!
Move constructed Thing!

with make_unique:
Copy constructed Thing!
Move constructed Thing!
Move constructed Thing!

with SetF:
Copy constructed Thing!
Move constructed Thing!

PS:我用 C++11 和 14 标记了这个问题,因为使用此处找到的常用 make_unique 函数将 C++11 标志传递给 gcc 时会发生同样的问题 (make_unique and perfect forwarding)

最佳答案

我认为使用 make_unique 时的附加步骤是由于在 Event(std::bind(lambda,thing))移动省略 .

Event 的构造函数被称为Event(function<void()> && f) ,所以临时 function<void()>必须创建。这个临时文件是用 std::bind 的返回值初始化的表达。

用于从 std::bind 的返回类型执行此转换的构造函数至 std::function<void()> 按值获取参数:

template<class F> function(F f); // ctor

这意味着我们必须移动 std::bind 的返回值到这个参数 f function<void()> 的构造函数.但是该移动符合移动省略的条件。

当我们通过 make_unique 传递那个临时值时,它已经绑定(bind)到一个引用,移动省略可能不再适用。如果我们因此抑制移动省略:

std::cout << "with unique_ptr, no make_unique\n";
unique_ptr<Event> ev_p(new Event(suppress_elision(std::bind(lambda,thing))));
std::cout << "\n";

std::cout << "with make_unique: \n";
auto ev_ptr = make_unique<Event>(suppress_elision(std::bind(lambda,thing)));
std::cout << "\n";

(我们可以使用 std::move 作为 suppress_elision 的实现。)

我们可以观察到相同的移动次数:Live example


解释整套操作:

对于 new Event(std::bind(lambda,thing)) :

operation                                             | behaviour ------------------------------------------------------+----------`thing` variable        ->  `bind` temporary          | copies`bind` temporary        ->  `function` ctor param     | moves (*)`function` ctor param   ->  `function` object (temp)  | moves`function` temporary    ->  `Event` ctor ref param    | -`Event` ctor ref param  ->  `function` data member    | *can* move (+)

(*)可以省略
(+) 但没有,可能是因为内部函数对象在堆上,并且只移动了一个指针。 Verify by replacing m_f(std::move(f)) with m_f() .

对于 make_unique<Event>(std::bind(lambda,thing)) :

operation                                               | behaviour --------------------------------------------------------+----------`thing` variable         ->  `bind` temporary           | copies`bind` temporary         ->  `make_unique` ref param    | -`make_unique` ref param  ->  `function` ctor param      | moves`function` ctor param    ->  `function` object (temp)   | moves`function` temporary     ->  `Event` ctor ref param     | -`Event` ctor ref param   ->  `function` data member     | *can* move (+)

关于c++ - 为什么 make_unique 有一个可以将 std::bind 作为参数的构造函数的额外移动?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27952709/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com