【发布时间】:2021-09-27 20:29:05
【问题描述】:
我正在尝试编写一些代码,以便通过将函数调用及其参数存储在 lambda/std::function 中来稍后调用函数。理想情况下,参数只会被复制一次(然后移动),但我能实现的最小副本数似乎是 2。
//==============================================================================
// INCLUDES
//==============================================================================
#include <iostream>
#include <functional>
#include <memory>
//==============================================================================
// VARIABLES
//==============================================================================
static std::unique_ptr<std::function<void()>> queueFunction;
//==============================================================================
// CLASSES
//==============================================================================
class Test {
public:
Test(int a, int b = 20, int c = 30) : _a(a), _b(b), _c(c) {
std::cout << "Test: Constructor" << std::endl;
}
~Test() {
std::cout << "Test: Destructor" << std::endl;
}
Test(const Test& other) :
_a(other._a)
{
std::cout << "Test: Copy Constructor" << std::endl;
}
Test(Test&& other) :
_a(std::move(other._a))
{
std::cout << "Test: Move Constructor" << std::endl;
}
Test& operator=(const Test& other) {
if (this != &other) {
_a = other._a;
std::cout << "Test: Assignment Operator" << std::endl;
}
return *this;
}
Test& operator=(Test&& other) {
if (this != &other) {
_a = std::move(other._a);
std::cout << "Test: Move Assignment Operator" << std::endl;
}
return *this;
}
friend std::ostream& operator<<(std::ostream& os, const Test& v) {
os << "{a=" << v._a << "}";
return os;
}
private:
int _a;
int _b;
int _c;
};
//==============================================================================
// FUNCTIONS
//==============================================================================
void foo(const Test& t);
void _foo(const Test& t);
template <typename F>
void queue(F&& fn) {
std::cout << "queue()" << std::endl;
queueFunction = std::make_unique<std::function<void()>>(std::forward<F>(fn));
}
void dequeue() {
std::cout << "dequeue()" << std::endl;
if (queueFunction) {
(*queueFunction)();
}
queueFunction.reset();
}
void foo(const Test& t) {
std::cout << "foo()" << std::endl;
queue([t](){
_foo(t);
});
//Only a single copy of Test is made here
/*
[t](){
_foo(t);
}();
*/
}
void _foo(const Test& t) {
std::cout << "_foo()" << std::endl;
std::cout << "t=" << t << std::endl;
}
//==============================================================================
// MAIN
//==============================================================================
int main() {
std::cout << "main()" << std::endl;
Test test1(20);
foo(test1);
dequeue();
std::cout << "main() return" << std::endl;
return 0;
}
以上代码的输出为:
main()
Test: Constructor
foo()
Test: Copy Constructor
queue()
Test: Copy Constructor
Test: Copy Constructor
Test: Destructor
Test: Destructor
dequeue()
_foo()
t={a=20}
Test: Destructor
main() return
Test: Destructor
这对我来说毫无意义。 lambda 不应该捕获一次 Test 的实例,然后将该 lambda 一直转发到新的 std::function 从而导致移动吗?
如果我这样修改队列函数,我至少可以摆脱一次副本。
void queue(std::function<void()> fn) {
std::cout << "queue()" << std::endl;
queueFunction = std::make_unique<std::function<void()>>(std::move(fn));
}
输出:
main()
Test: Constructor
foo()
Test: Copy Constructor
Test: Copy Constructor
queue()
Test: Destructor
dequeue()
_foo()
t={a=20}
Test: Destructor
main() return
Test: Destructor
但我仍然不明白多余的副本是从哪里来的。
谁能帮我解惑?
【问题讨论】:
-
你在使用Visual Studio吗?
-
@TedLyngmo 不,我只是在使用 gcc(8.1.0 版)
-
好吧,奇怪。我在 VS 上写了一份故障报告(上面的链接),因为我发现它在一个
std::function的信号分配上复制了 1 次 + 移动了 4 次。对于 gcc 和 clang,我找到了 1 个副本 + 1 个移动。如果您在我的链接中运行程序,您的 gcc 会得到什么?我刚刚注意到最新的 gcc 只管理一个副本,没有移动。 -
@TedLyngmo 我得到以下输出: Foo() Foo() -- 构造 func -- Foo(const Foo&) Foo(Foo&&) ~Foo() 1 -- 分配给 func -- Foo (const Foo&) Foo(Foo&&) ~Foo() 2 ~Foo() 3 -- 程序结束 -- ~Foo() 4 ~Foo() 5 ~Foo() 6 Foo 析构函数调用了 6 次
-
好的,复制分配时1复制+1移动。这和我得到的一样。如果你移动分配,我猜你会得到 2 次移动。他们似乎在最新的 gcc 中对此进行了改进。现在只完成一个副本或一个移动。
标签: c++ lambda std-function