草庐IT

c++ - 将 C++11 move 语义应用于绑定(bind)函数

coder 2024-02-25 原文

我有一些现有的 C++98 代码,它们使用 boost::functionboost:bind 进行异步回调。一些相关的简化代码片段包括:

typedef boost::function<void (boost::system::error_code, size_t)> WriteHandler;

struct WriteOperation
{
    WriteOperation(const boost::shared_ptr<IDevice>& device,
                   const std::string& data, const WriteHandler& handler)
        : m_Device(device), m_Data(data), m_Handler(handler) {}

private:
    boost::shared_ptr<IDevice> m_Device;
    std::string m_Data;
    WriteHandler m_Handler;

    void Complete()
    {
        boost::system::error_code ec;
        size_t len;
        ...
        Async::Post(boost::bind(m_Handler, ec, len));
    }
};

struct Device : public IDevice
{
    void Write(const std::string& data, const WriteHandler& callback)
    {
        ...
        Async::Start(new WriteOperation(shared_from_this(), data,
            boost::bind(&Device::HandleWrite, this, handler, _1, _2)));
    }

 private:
    void HandleWrite(const WriteHandler& callback,
                     boost::system::error_code ec, size_t len)
    {
        ...
        callback(ec, len);
    }
};

在构造 WriteOperation 时需要一个拷贝,但除此之外我尽量避免拷贝,因为它们可能非常昂贵。

我正在考虑如何在 C++11 世界中最好地编写它。显而易见的容易实现的成果是 WriteOperation 构造函数在内部将其参数复制到其字段,因此应该使用自动复制习惯用法:

WriteOperation(boost::shared_ptr<IDevice> device,
               std::string data, WriteHandler handler)
    : m_Device(std::move(device)), m_Data(std::move(data)), m_Handler(std::move(handler))
{}

(当然,裸露的 new 应该替换为 unique_ptr,但这是一个附带问题。)

但是,鉴于当前 Device::Write 的实现,我不认为这实际上有任何好处,所以这也应该改变。我的问题是我真的没有看到一个好的方法来做到这一点。根据this advice ,我有三个选择:

  1. 声明多个重载(一个使用 const&,一个使用 &&)——但是因为它有两个参数,这两个参数都可以从 move 语义中获益,这将需要四次重载——对于具有更多参数的方法,情况会呈指数级恶化。此外,这会导致代码重复或将代码散布在其他方法上,从而影响可读性。

  2. 按值传递并 move (类似于WriteOperation 构造函数)。这可能是当正文总是制作拷贝时最干净的选项,如果实际调用了 WriteOperation 构造函数,这是正确的,但是如果省略的部分包含可能返回而不构造 WriteOperation 的逻辑怎么办?在这种情况下有一个浪费的拷贝。

  3. 模板和完美转发。这需要一个丑陋的 SFINAE hack,它会混淆 Intellisense 并削弱可读性(或者更糟,使参数类型不受约束),并且需要将实现放入 header 中,这有时是不可取的。 并且它会干扰类型转换,例如。寻找 std::string 的 SFINAE enable_if is_same 将不接受 const char * 文字,而原始的const& 版本会。

我错过了什么吗?有更好的解决方案吗?或者这只是 move 语义没有任何区别的情况?


相关案例:

typedef boost::function<void (boost::system::error_code, const std::string&)> ReadHandler;

void Read(const ReadHandler& callback)
{
    ... boost::bind(&Device::HandleRead, this, callback, _1, _2) ...
}

void HandleRead(const ReadHandler& callback,
                boost::system::error_code ec, const std::string& data)
{
    ...
    callback(ec, data);
}

这一切看起来应该没问题,没有复制也不需要 move 语义。我再次不确定传递给 ReadReadHandler

最佳答案

大致顺序:

如果复制和 move 一样昂贵,请用 const& .

如果您可靠地保留一份拷贝,并且搬家很便宜,请按值(value)获取。

如果做不到这一点,您可以将其填充到标题中,并且可以使用 sfinae 或不受约束的模板,使用转发引用。

否则,如果参数数量有限,请编写每个重载。这是 2^n在参数的数量上,所以最好不要太多。在内部转发到基于转发引用的实现。

否则,您真的需要效率吗?

否则,输入 erase 到“T 的创建者”。

template<class T, using Base=std::function<T()>>
struct creator_of: Base
{
  template<class U,
    std::enable_if_t<std::is_constructible<T, U&&>{},int> =0
  >
  creator_of(U&&u):
    Base([&]()->T{ return std::forward<U>(u); })
  {}
  template<class U,
    std::enable_if_t<std::is_constructible<T, std::result_of_t<std::decay_t<U>()>{},int> =0
  >
  creator_of(U&&u):
    Base(std::forward<U>(u))
  {}

  creator_of(creator_of&&)=default;
  creator_of():
    Base([]()->T{return {};}}
  {}
};

根据需要增加。 creator_of<std::string>可以由可以构建 std::string 的事物构建,或来自返回 std::string 的函数对象.

内部您可以调用()一次就可以了。

(代码未编译,但设计合理。)

关于c++ - 将 C++11 move 语义应用于绑定(bind)函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39950667/

有关c++ - 将 C++11 move 语义应用于绑定(bind)函数的更多相关文章

  1. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  2. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  3. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  4. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  5. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  6. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  7. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  8. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  9. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  10. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

随机推荐