草庐IT

c++ - 语法糖 : automatically creating simple function objects

coder 2024-02-17 原文

我要实现一组类模板和两个特殊变量,_1_2 .

他们应该使以下内容成为合法代码:

// Sort ascending
std::sort(a, a+5, _1 > _2);

// Output to a stream
std::for_each(a, a+5, std::cout << _1 << " ");

// Assign 100 to each element
std::for_each(a, a+5, _1 = 100);

// Print elements increased by five 5
std::transform(a, a+5, std::ostream_iterator<int>(std::cout, " "), _1 + 5);

我想 _1 * 5 也应该产生一个一元函数,以及 _1/5 等。
  • 不允许提升
  • 不允许使用 lambda

  • 现在我有 非常对模板和模板元编程的经验很少,所以我什至不知道从哪里开始以及我的类模板的结构应该是什么样子。我特别困惑,因为我不知道在我的类模板中是否必须为所有这些编写实现 operator= , operator>> , operator+ , ...- , ...* , .../单独 - 或者有更通用的方法来做到这一点。

    我将特别感谢提供这些运算符实现示例的答案;模板对我来说仍然是一团糟。

    最佳答案

    好!这确实是一个棘手的家庭作业问题!但是,这也是一个很好的工作和学习问题。

    我认为回答这个问题的最好方法是让您从简单的用例开始,然后逐步构建您的解决方案。

    例如,假设您有以下 std::vector<int>跟...共事:

    std::vector<int> vec;
    vec.push_back(4);
    vec.push_back(-8);
    vec.push_back(1);
    vec.push_back(0);
    vec.push_back(7);
    

    您显然希望允许以下用例:
    std::for_each(vec.cbegin(), vec.cend(), _1);
    

    但是如何允许呢?首先你需要定义 _1然后您需要为 _1 类型的函数调用运算符实现“任何事情都可以”重载.

    Boost Lambda 和 Boost Bind 定义占位符对象的方式 _1 , _2 , ... 是让他们有一个虚拟类型。例如,_1对象可能具有类型 placeholder1_t :
    struct placeholder1_t { };
    placeholder1_t _1;
    
    struct placeholder2_t { };
    placeholder2_t _2;
    

    这种“虚拟类型”经常被非正式地称为标签类型。有许多 C++ 库和 STL 依赖于标签类型(例如 std::nothrow_t )。它们用于选择要执行的“正确”函数重载。本质上,创建具有标签类型的虚拟对象并将它们传递到函数中。该函数不以任何方式使用虚拟对象(实际上,大多数情况下甚至没有为其指定参数名称),但是由于存在该额外参数,编译器能够选择正确的重载来调用.

    让我们扩展 placeholder1_t 的定义通过添加函数调用运算符的重载。请记住,我们希望它接受任何内容,因此函数调用运算符的重载本身将根据参数类型进行模板化:
    struct placeholder1_t
    {
        template <typename ArgT>
        ArgT& operator()(ArgT& arg) const {
            return arg;
        }
    
        template <typename ArgT>
        const ArgT& operator()(const ArgT& arg) const {
            return arg;
        }
    };
    

    而已!我们最简单的用例现在将编译并运行:
    std::for_each(vec.cbegin(), vec.cend(), _1);
    

    当然,它基本上相当于无操作。

    现在让我们处理 _1 + 5 .那个表情应该做什么?它应该返回一个一元函数对象,当用一个参数(某种未知类型)调用时,结果是该参数加 5。为了使它更通用,表达式是一元函数对象 +目的。返回的对象本身就是一个一元函数对象。

    需要定义返回对象的类型。它将是一个带有两个模板类型参数的模板:一元函数类型和被添加到一元函数结果中的对象类型:
    template <typename UnaryFnT, typename ObjT>
    struct unary_plus_object_partfn_t;
    

    “partfn”是指表示二进制 + 部分应用的函数类型运算符(operator)。这种类型的实例需要一元函数对象(类型为 UnaryFnT )和另一个对象(类型为 ObjT )的拷贝:
    template <typename UnaryFnT, typename ObjT>
    struct unary_plus_object_partfn_t
    {
        UnaryFnT m_fn;
        ObjT m_obj;
    
        unary_plus_object_partfn_t(UnaryFnT fn, ObjT obj)
            : m_fn(fn), m_obj(obj)
        {
        }
    };
    

    好的。函数调用运算符也需要重载以允许任何参数。我们将使用 C++11 decltype特征来引用表达式的类型,因为我们事先不知道它是什么:
    template <typename UnaryFnT, typename ObjT>
    struct unary_plus_object_partfn_t
    {
        UnaryFnT m_fn;
        ObjT m_obj;
    
        unary_plus_object_partfn_t(UnaryFnT fn, ObjT obj)
            : m_fn(fn), m_obj(obj)
        {
        }
    
        template <typename ArgT>
        auto operator()(ArgT& arg) const -> decltype(m_fn(arg) + m_obj) {
            return m_fn(arg) + m_obj;
        }
    
        template <typename ArgT>
        auto operator()(const ArgT& arg) const -> decltype(m_fn(arg) + m_obj) {
            return m_fn(arg) + m_obj;
        }
    };
    

    它开始变得复杂,但这段代码并没有什么惊喜。它本质上是说函数调用运算符被重载以接受几乎任何参数。然后它会调用 m_fn (一元函数对象)在参数上并添加 m_obj结果。返回类型是 m_fn(arg) + m_obj 的 decltype .

    现在定义了类型,我们可以编写二元运算符 + 的重载接受类型为 placeholder1_t 的对象在左侧:
    template <typename ObjT>
    inline unary_plus_object_partfn_t<placeholder1_t, ObjT> operator+(const placeholder1_t& fn, ObjT obj)
    {
        return unary_plus_object_partfn_t<placeholder1_t, ObjT>(fn, obj);
    }
    

    我们现在可以编译并运行第二个用例:
    std::transform(vec.cbegin(), vec.cend(), std::ostream_iterator<int>(std::cout, " "), _1 + 5);
    std::cout << std::endl;
    

    输出:
    9 -3 6 5 12
    

    This is basically all that you need to do to solve the problem. Think about how you can write custom functional types, instances of which can be returned by overloads of operators.

    EDIT: Improved the overloads of function call operators by employing pass-by-reference.

    EDIT2: In some cases it will be necessary to store a reference to an object rather than a copy of it. For example, to accommodate std::cout << _1, you will need to store a reference to std::cout in the resulting functional object because the std::ios_base copy constructor is private, and it is impossible to copy construct objects of any class derived from std::ios_base including std::ostream.

    To allow for std::cout << _1, you might want to write a ref_insert_unary_partfn_t template. Such a template, like the example of unary_plus_object_partfn_t above, would be templated on an object type and a unary functional type:

    template <typename ObjT, typename UnaryFnT>
    struct ref_insert_unary_partfn_t;
    

    此模板的实例化实例需要存储对类型 ObjT 的对象的引用。以及类型为 UnaryFnT 的一元函数对象的拷贝:
    template <typename ObjT, typename UnaryFnT>
    struct ref_insert_unary_partfn_t
    {
        ObjT& m_ref;
        UnaryFnT m_fn;
    
        ref_insert_unary_partfn_t(ObjT& ref, UnaryFnT fn)
            : m_ref(ref), m_fn(fn)
        {
        }
    };
    

    像以前一样添加函数调用运算符的重载以及插入运算符的重载,<< .

    std::cout << _1的情况下,返回的对象的类型为 ref_insert_unary_partfn_t<std::basic_ostream<char>, placeholder1_t> .

    关于c++ - 语法糖 : automatically creating simple function objects,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8447680/

    有关c++ - 语法糖 : automatically creating simple function objects的更多相关文章

    1. ruby - 树顶语法无限循环 - 2

      我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He

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

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

    3. ruby-on-rails - 使用 Sublime Text 3 突出显示 HTML 背景语法中的 ERB? - 2

      所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择

    4. ruby - 覆盖相似的方法,更短的语法 - 2

      在Ruby类中,我重写了三个方法,并且在每个方法中,我基本上做同样的事情:classExampleClassdefconfirmation_required?is_allowed&&superenddefpostpone_email_change?is_allowed&&superenddefreconfirmation_required?is_allowed&&superendend有更简洁的语法吗?如何缩短代码? 最佳答案 如何使用别名?classExampleClassdefconfirmation_required?is_a

    5. ruby 语法糖 : dealing with nils - 2

      可能已经问过了,但我找不到它。这里有2个常见的情况(对我来说,在编程Rails时......)用ruby​​编写是令人沮丧的:"astring".match(/abc(.+)abc/)[1]在这种情况下,我得到一个错误,因为字符串不匹配,因此在nil上调用[]运算符。我想找到的是比以下内容更好的替代方法:temp="astring".match(/abc(.+)abc/);temp.nil??nil:temp[1]简而言之,如果不匹配,则简单地返回nil而不会出错第二种情况是这样的:var=something.very.long.and.tedious.to.writevar=some

    6. ruby - Ruby 语法糖有 "rules"吗? - 2

      我正在学习Ruby的基础知识(刚刚开始),我遇到了Hash.[]method.它被引入a=["foo",1,"bar",2]=>["foo",1,"bar",2]Hash[*a]=>{"foo"=>1,"bar"=>2}稍加思索,我发现Hash[*a]等同于Hash.[](*a)或Hash.[]*一个。我的问题是为什么会这样。是什么让您将*a放在方括号内,是否有某种规则可以在何时何地使用“it”?编辑:我的措辞似乎造成了一些困惑。我不是在问数组扩展。我明白了。我的问题基本上是:如果[]是方法名称,为什么可以将参数放在括号内?这看起来几乎——但不完全是——就像说如果你有一个方法Foo.d

    7. ruby - 使用 `+=` 和 `send` 方法 - 2

      如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

    8. ruby - 如何让Ruby捕获线程中的语法错误 - 2

      我正在尝试使用ruby​​编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?

    9. ruby -::在 Ruby 语法中是什么意思? - 2

      这个问题在这里已经有了答案:WhatisRuby'sdouble-colon`::`?(12个答案)关闭8年前。什么是::?@song||=::TwelveDaysSong.new

    10. ruby - ruby 乘法语句中星号中断语法前的空格 - 2

      在添加一些空格以使代码更具可读性时(与上面的代码对齐),我遇到了这个:classCdefx42endendm=C.new现在这将给出“错误数量的参数”:m.x*m.x这将给出“语法错误,意外的tSTAR,期待$end”:2/m.x*m.x这里的解析器到底发生了什么?我使用Ruby1.9.2和2.1.5进行了测试。 最佳答案 *用于运算符(42*42)和参数解包(myfun*[42,42])。当你这样做时:m.x*m.x2/m.x*m.xRuby将此解释为参数解包,而不是*运算符(即乘法)。如果您不熟悉它,参数解包(有时也称为“spl

    随机推荐