我这里有一个对象名叫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 编译标准
|  |      1ysc3839      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&) 而出错 | 
|      2statumer      2022-03-24 18:18:37 +08:00 via iPhone  3 std::bind 会把你的右值引用存为左值(一个成员变量),但是左值无法被 SampleFunction 接收,所以你这个 bind 是无效的,完全无法被调用。 | 
|      3darklights      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. 以上这代码非常危险,切勿模仿。 | 
|      4codehero      2022-03-24 19:15:34 +08:00 @darklights  应该是 2 楼说的原因, bind 把你的参数存成了左值(类型还是右值引用, 这两个不一样), 左值是不会调用 move 构造的, 所以有问题. 你改之后的版本没问题是因为是没用 bind, 且使用了 std::move 将参数变成了右值↓↓↓ void operator()() { SampleFunction(0, std::move(_target)); } }; | 
|  |      5yujincheng08      2022-03-24 19:23:07 +08:00 目测是以下问题: `std::function` 要求可以复制,如果把 `Sample` 对象放进去了,那 `std::function` 就无法复制了。 要么让 `Sample` 可复制,要么用 `std::move_only_function` | 
|      6darklights      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 了。 | 
|  |      7FrankHB      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 的代码膨胀之类乱七八糟的屑问题。 | 
|  |      8YUCOAT OP @darklights 那用什么替代呢 | 
|      9codehero      2022-03-25 11:02:59 +08:00 |