绑定可变参数模板参数时副本过多
Too many copies when binding variadic template arguments
我正在创建一个作业队列。作业将在线程 A 中创建,然后将作业发送到线程 B,线程 B 将完成作业。作业完成后,作业将被发送回线程 A。
#include <functional>
#include <iostream>
#include <memory>
using namespace std;
template<typename T, typename... Args>
class Job
{
public:
Job(std::weak_ptr<T> &&wp, std::function<void(const Args&...)> &&cb)
: _cb(std::move(cb)),
_cbWithArgs(),
_owner(std::move(wp)) {}
public:
template<typename... RfTs>
void bind(RfTs&&... args)
{
// bind will copy args for three times.
_cbWithArgs = std::bind(_cb, std::forward<RfTs>(args)...);
}
void fire()
{
auto sp = _owner.lock();
if (sp)
{
_cbWithArgs();
}
}
private:
std::function<void(const Args& ...)> _cb;
std::function<void()> _cbWithArgs;
std::weak_ptr<T> _owner;
};
struct Args
{
Args() = default;
Args(const Args &args)
{
cout << "Copied" << endl;
}
};
struct Foo
{
void show(const Args &)
{
cout << "Foo" << endl;
}
};
int main()
{
using namespace std::placeholders;
shared_ptr<Foo> sf (new Foo());
Args args;
// Let's say here thread A created the job.
Job<Foo, Args> job(sf, std::bind(&Foo::show, sf.get(), _1));
// Here thread B has finished the job and bind the result to the
// job.
job.bind(args);
// Here, thread A will check the result.
job.fire();
}
上面的代码编译和工作。但它给出了以下结果(g++ 4.8.4 和 clang 有相同的结果(:
Copied
Copied
Copied
Foo
有三份!不可接受,我不知道我哪里做错了。为什么是三份?我用谷歌搜索并从这里找到了一种方法:https://stackoverflow.com/a/16868401,它只复制一次参数。但它必须在构造函数中初始化绑定函数。
谢谢,彼得·斯科特尼基。不幸的是,我没有 C++14 编译器。因此,让我们使 Args 可移动:
struct Args
{
Args() = default;
Args(const Args &args)
{
cout << "Copied" << endl;
}
Args(Args &&) = default;
Args& operator=(Args &&) = default;
};
现在,它只复制一次:)
最后,我采用了这个线程 https://stackoverflow.com/a/16868151/5459549 的代码。我必须说,模板gen_seq是真正的艺术。
第一个副本由std::bind
自己制作:
std::bind(_cb, std::forward<RfTs>(args)...)
因为它需要存储其参数的衰减副本。
另外两份副本由转让给_cbWithArgs
制作,类型为std::function
(§ 20.8.11.2.1 [func.wrap.func.con](:
template<class F> function& operator=(F&& f);
18效果:
function(std::forward<F>(f)).swap(*this);
哪里:
template<class F> function(F f);
9 [...]
*this
以使用std::move(f)
初始化的f
的副本为目标。
也就是说,一个副本用于构造函数的参数,另一个副本用于存储参数f
。
由于Args
是不可移动的类型,因此尝试从调用的结果移动到std::bind
将回退到副本。
在您的代码中将作业执行的结果存储在std::function
中似乎不合理。相反,您可以将这些值存储在元组中:
template <typename T, typename... Args>
class Job
{
public:
Job(std::weak_ptr<T> wp, std::function<void(const Args&...)> &&cb)
: _cb(std::move(cb)),
_owner(wp) {}
public:
template<typename... RfTs>
void bind(RfTs&&... args)
{
_args = std::forward_as_tuple(std::forward<RfTs>(args)...);
}
void fire()
{
auto sp = _owner.lock();
if (sp)
{
apply(std::index_sequence_for<Args...>{});
}
}
private:
template <std::size_t... Is>
void apply(std::index_sequence<Is...>)
{
_cb(std::get<Is>(_args)...);
}
std::function<void(const Args&...)> _cb;
std::weak_ptr<T> _owner;
std::tuple<Args...> _args;
};
演示
- 如何反转整数参数包
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 如何使用默认参数等选择模板专业化
- 模板参数替换失败,并且未完成隐式转换
- 具有默认模板参数的多态类的模板推导失败
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 副本初始化的默认模板参数推导
- C ++引用函数参数似乎包含原始对象的副本,而不是充当"real reference"
- 为什么没有为函数参数删除副本
- 为什么函数对实际参数的副本进行操作
- std::可选参数会创建副本吗?
- 有条件的操作员从其参数的副本中返回值
- 将参数传递给const在内存中的副本的功能
- 参考参数返回副本
- 具有未定义参数的私有副本构造函数
- C++ 指针参数:创建指向的值的副本,然后再次将副本存储为指针是否有意义
- 如果参数创建本地副本,则使用参数"const [variable_type] &"的目的是什么?
- 类副本构造不能具有类类型的参数
- 绑定可变参数模板参数时副本过多
- 面试编码-将指向节点结构的指针作为参数,并返回传入数据结构的完整副本