我这里有一个对象名叫Sample
,它不支持拷贝赋值与拷贝构造
,但是支持转移赋值和转移构造
。
现在我需要创建一个std::function<void()>
函数对象,其中的参数就有Sample
对象,
我的代码是这样写的:
#include <functional>
struct Sample {
Sample(Sample&& p) {
m_pData = p.m_pData;
p.m_pData = nullptr;
};
static Sample Create(void* data) {
return { data };
}
Sample& operator=(Sample&& p)
{
m_pData = p.m_pData;
p.m_pData = nullptr;
return *this;
}
private:
Sample(void* data) { m_pData = data; }
private:
Sample(const Sample&) = delete;
Sample& operator=(const Sample&) = delete;
void* m_pData;
};
void SampleFunction(int i, Sample f)
{
}
int main()
{
Sample s = Sample::Create(nullptr);
std::function<void()> f2 = std::bind(SampleFunction, 0, std::move(s));
return 0;
}
结果编译出错了,错误信息是:
error C2440: “初始化”: 无法从“std::_Binder<std::_Unforced,void (__cdecl &)(int,Sample),int,Sample>”转换为“std::function<void (void)>”
请问这是什么原因?以及怎么改正? 编译环境是 Windows + vs2015 C++14 编译标准
1
ysc3839 2022-03-24 18:06:56 +08:00 1
我觉得是因为 std::function 内部会拷贝
改成这样是没问题的: ``` auto f2 = [s = std::move(s)]() mutable { SampleFunction(0, std::move(s)); }; ``` 把 auto 换成 std::function<void()> 后就会因为使用了 Sample(const Sample&) 而出错 |
2
statumer 2022-03-24 18:18:37 +08:00 via iPhone 3
std::bind 会把你的右值引用存为左值(一个成员变量),但是左值无法被 SampleFunction 接收,所以你这个 bind 是无效的,完全无法被调用。
|
3
darklights 2022-03-24 18:46:19 +08:00
void SampleFunction(int, Sample)
{ } struct SampleFunctor { mutable Sample _target; SampleFunctor(Sample&& target) : _target(std::move(target)) {} SampleFunctor(const SampleFunctor& rhs) : _target(std::move(rhs._target)) {} SampleFunctor(SampleFunctor&& rhs) : _target(std::move(rhs._target)) {} void operator()() { SampleFunction(0, std::move(_target)); } }; int main() { Sample s = Sample::Create(nullptr); std::function<void()> f { SampleFunctor{ std::move(s) } }; return 0; } ~~~~~~~~ 经 1 楼启发,改成以上能通过编译。因为 std::function 是可复制的,所以它的 target 也必须是可复制,应该是属于那种“就算用不到但必须提供”。 From cppreference:std::decay<F>::type must meet the requirements of Callable and CopyConstructible. 以上这代码非常危险,切勿模仿。 |
4
codehero 2022-03-24 19:15:34 +08:00
@darklights
应该是 2 楼说的原因, bind 把你的参数存成了左值(类型还是右值引用, 这两个不一样), 左值是不会调用 move 构造的, 所以有问题. 你改之后的版本没问题是因为是没用 bind, 且使用了 std::move 将参数变成了右值↓↓↓ void operator()() { SampleFunction(0, std::move(_target)); } }; |
5
yujincheng08 2022-03-24 19:23:07 +08:00
目测是以下问题:
`std::function` 要求可以复制,如果把 `Sample` 对象放进去了,那 `std::function` 就无法复制了。 要么让 `Sample` 可复制,要么用 `std::move_only_function` |
6
darklights 2022-03-24 19:38:56 +08:00
@codehero
我回答的是“以及怎么改正”。 一开始我试的就是 1 楼说的:std::function<void()> f2 = [s = std::move(s)]() mutable { SampleFunction(0, std::move(s)); }; 照样炸。只是当时没明白为何炸。 2022 没人用 bind 了。 |
7
FrankHB 2022-03-24 23:54:44 +08:00
@darklights 只有 C++11 能用,不用 bind 怎么模拟 capture-init ?自己写?
也不是没理由自己造,比如 wg21.link/p0826 。但是这种问题遇到的概率比你 function 的坑小得多,类似的逻辑就更没理由去用 std::function 。 作为通用目的的 call wrapper ,std::function 的 CopyConstructible 要求本来就是个二缺设计,跟核心语言的 copy initialization 要求格格不入。最欠的是这种要求直接写在了 ctor template 里,所以用户能保证自己不 copy 也会被坑。 反正我是选择自己糊: github.com/FrankHB/YSLib/blob/master/YBase/include/ystdex/function.hpp 正好干掉不支持分配器、不支持定制空调用行为和某些实现依赖 RTTI 的代码膨胀之类乱七八糟的屑问题。 |
8
YUCOAT OP @darklights 那用什么替代呢
|
9
codehero 2022-03-25 11:02:59 +08:00
|