请问这段 C++代码为什么会编译不过 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
YUCOAT
V2EX    程序员

请问这段 C++代码为什么会编译不过

  •  
  •   YUCOAT 2022-03-24 17:43:39 +08:00 2841 次点击
    这是一个创建于 1364 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我这里有一个对象名叫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 条附言    2022-03-25 09:43:43 +08:00
    感谢大家的回答,我明白了。意思就是 std::function 它是需要支持拷贝的,而我的 Sample 不允许拷贝和赋值,所以我的代码编译不过。

    stl 这个地方设计得还是有点坑,这一点 Chrome 中的 base::Callback 设计得比较好,没有这个问题。
    10 条回复    2022-03-25 11:36:14 +08:00
    ysc3839
        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&) 而出错
    statumer
        2
    statumer  
       2022-03-24 18:18:37 +08:00 via iPhone   3
    std::bind 会把你的右值引用存为左值(一个成员变量),但是左值无法被 SampleFunction 接收,所以你这个 bind 是无效的,完全无法被调用。
    darklights
        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.

    以上这代码非常危险,切勿模仿。
    codehero
        4
    codehero  
       2022-03-24 19:15:34 +08:00
    @darklights
    应该是 2 楼说的原因, bind 把你的参数存成了左值(类型还是右值引用, 这两个不一样), 左值是不会调用 move 构造的, 所以有问题.

    你改之后的版本没问题是因为是没用 bind, 且使用了 std::move 将参数变成了右值↓↓↓
    void operator()() { SampleFunction(0, std::move(_target)); }
    };
    yujincheng08
        5
    yujincheng08  
       2022-03-24 19:23:07 +08:00
    目测是以下问题:
    `std::function` 要求可以复制,如果把 `Sample` 对象放进去了,那 `std::function` 就无法复制了。
    要么让 `Sample` 可复制,要么用 `std::move_only_function`
    darklights
        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 了。
    FrankHB
        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 的代码膨胀之类乱七八糟的屑问题。
    YUCOAT
        8
    YUCOAT  
    OP
       2022-03-25 09:45:05 +08:00
    @darklights 那用什么替代呢
    codehero
        9
    codehero  
       2022-03-25 11:02:59 +08:00
    @darklights
    不好意思, 把你看成题主了, 所以解释了一下原因

    还有, 没说让用 bind, 只是说 bind 是报错的其中一个原因
    icylogic
        10
    icylogic  
       2022-03-25 11:36:14 +08:00
    @YUCOAT C++14 以后大部分场景就可以用 lambda 替代了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5227 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 09:05 PVG 17:05 LAX 01:05 JFK 04:05
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86