草庐IT

c++ - 如何允许自定义返回类型的成员函数以在 C++ 中进行类型删除?

coder 2024-02-25 原文

在过去的几年中,类型删除基于概念的运行时多态性 变得非常流行,参见例如Sean Parent 的演讲 Better Code: Runtime Polymorphism , Inheritance Is The Base Class of EvilC++ Seasoning分别Adobe 的实现, Facebook , Boost.TypeErasure , Boost.tedyno .

这是 Sean Parent 演讲中的一个例子:

class object_t {
    struct concept_t {
        virtual ~concept_t() = default;
        virtual void draw_(ostream&, size_t) const = 0;
    };
    template <typename T>
    struct model final : concept_t {
        model(T x) : data_(move(x)) { }
        void draw_(ostream& out, size_t position) const override 
        { draw(data_, out, position); }
        T data_;
    };
    shared_ptr<const concept_t> self_;
public:
    template <typename T>
    object_t(T x) : self_(make_shared<model<T>>(move(x))){ }
    friend void draw(const object_t& x, ostream& out, size_t position)
    { x.self_->draw_(out, position); }    
};

在上面的例子中,任何类 T 都必须提供一个函数

void draw(T data, std::ostream out, size_t position);

这对任何 T 来说都很容易实现,因为返回类型是 void 并且参数签名在编译时都是已知的。如果返回类型为 int、double、std::string 等,情况也是如此。

这是真正的问题:How to do this for a custom return type RType resp.参数类型 ArgType?

class object_t {
    struct concept_t {
        virtual ~concept_t() = default;
        virtual void draw_(ostream&, size_t) const = 0;
        virtual RType foo (ArgType arg) const = 0; // new function
    };
    template <typename T>
    struct model final : concept_t {
        model(T x) : data_(move(x)) { }
        void draw_(ostream& out, size_t position) const override 
        { draw(data_, out, position); }
        RType foo (ArgType arg) const override 
        { return data_.foo(arg);}; // new function
        T data_;
    };
    shared_ptr<const concept_t> self_;
public:
    template <typename T>
    object_t(T x) : self_(make_shared<model<T>>(move(x))){ }
    friend void draw(const object_t& x, ostream& out, size_t position)
    { x.self_->draw_(out, position); }    
    RType foo (ArgType arg) const 
    { return self_->foo(arg);}; // new function
};

(i) 如果 RTypeArgType 是显式类型,即存在类型 class MyClass{}; 我们可以定义using RType = MyClass;using ArgType = MyClass 一切正常。

(ii) 如果 RTypeArgType 是基于概念的多态类型,例如 object_t,也可以。 但这意味着代码库将充满接口(interface)类,例如 object_t。不要误会我的意思,这是一个公平的代价,但需要一些时间来适应它并构建一个代码库来做到这一点。

现在是有问题的部分:如果我有这样的using RType = typename T::RTypeusing ArgType = typename T::ArgType,我能否使示例工作T???

class T {
public:
    using ArgType = /*...*/;
    using RType = /*...*/;
/*...*/
}; 

编辑:

最终目标是有两个类

class MyClass {
    using ArgType = /* e.g. int*/; 
    using RType = /* e.g. double*/;
    RType foo (ArgType arg) const {/*...*/;}
};

class YourClass {
    using ArgType = /* e.g. size_t*/; 
    using RType = /* e.g. float*/;
    RType foo (ArgType arg) const {/*...*/;}
};

这样我们就可以将两者与 object_t 一起使用,即

object_t myObj(MyClass(/*...*/));
object_t yourObj(YourClass(/*...*/));
// use provided functionality
// ... myObj.foo(...)
// ... yourObj.foo(...)

最佳答案

当然 object_t::foo 的任何一个重载必须有一个单一的返回类型,并且有充分的理由:如果客户端可以接收一个类型的对象,该类型只存在于创建 object_t 的其他翻译单元中。 ,编译器怎么可能生成代码来处理它?如果存在某种可以将所有各种返回类型转换成的类型,您可以将其用作外部返回类型,但这肯定太局限了。

当然,您可以提供 foo至少接受任何类型的模板函数,但您将如何实现它?函数模板不能是 virtual ,出于类似的原因,无法从任何一个翻译单元知道(典型的)vtable 的大小。您可以将几种已知类型中的每一种路由到其自己的虚函数,但必须事先知道此类函数的列表;或者,您可以再次依赖将任何参数转换为某种已知类型,但模板的客户端可以自己完成。

唯一的其他选择是为参数和返回值定义一个接口(interface),以针对它提供和编写所有实现类和客户端代码。这当然是许多脚本语言的工作原理:本质上,CPython 中的每个内置函数都有签名

PyObject* func(PyObject*);

因此,即使是任意 Python 数据的有状态 C++ 可访问操纵器也可以存储在 std::function<PyObject*(PyObject*)> 中。无需定义您自己的类型删除类。

关于c++ - 如何允许自定义返回类型的成员函数以在 C++ 中进行类型删除?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52525173/

有关c++ - 如何允许自定义返回类型的成员函数以在 C++ 中进行类型删除?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  5. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  6. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  7. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  8. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  9. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  10. ruby - 我可以使用 Ruby 从 CSV 中删除列吗? - 2

    查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html

随机推荐