有没有办法用一行在同一个对象上调用多个函数?

时间:2016-10-03 19:07:29

标签: c++ method-chaining syntactic-sugar function-calls member-functions

只是想整理一个程序,并想知道是否有人可以在同一行上多次调用一个队列上的成员函数来提供一些语法糖。

例如,更改:

queue<int> q;
q.push(0);
q.push(1);

类似于:

q.(push(0), push(1));
//or
q.push(0).push(1);

我知道它看起来有点荒谬,并不实用。但是,如果我想缩短这样的一小部分代码,是否可以选择这样做?从我到目前为止所读到的内容来看,当函数具有非void返回值时,只能链接方法。

当然,这是一个选择:

q.push(0); q.push(1);

但我试图避免两次q。再次......语法糖:))

这里的目标不是初始化,而是压缩对象/容器在代码块中出现的次数。我引用队列的原因是因为它是动态的。

6 个答案:

答案 0 :(得分:21)

如果您有可以修改的类,请使该函数返回对自身的引用:

template<typename T>
class queue {
public:
    //...
    queue& push(T data) {
        //...
        return *this; //return current instance
    }
    //...
private:
    //...
};

然后你可以做

queue<int> q;
q.push(0).push(1);

如果你不能,那么你的双手被束缚住了。你可以在课堂上制作一个包装器,但为了节省一些角色,这几乎不值得付出努力。

对于push,您可以执行以下操作:

queue<int> q = { 0, 1 };

但这显然只适用于push,因为队列在推送后会包含01

答案 1 :(得分:10)

你总是可以定义一个包装器,比如

template< class Item >
void push( queue<Item>& q, std::initializer_list<Item> const& values )
{
    for( Item const& v : values ) { q.push( v ); }
}

然后这样称呼:

push( q, {1, 2, 3} );

如果您想要的不仅仅是符号方便,而只是使用流畅的界面技术,那么如果您无法修改类,请定义一个运算符:

template< class Item >
auto operator<<( queue<Item>& q, Item v )
    -> queue<Item>&
{ q.push( move( v ) ); return q; }

然后这样称呼:

q << 1 << 2 << 3;

请务必记录您的同事试图掌握代码。 :)

哦,好吧,如果你不能修改课程,你当然可以这样做:

template< class Item >
struct Fluent
{
    queue<Item>& items;

    auto push( Item v )
        -> Fluent&
    { items.push( move( v ) ); return *this; }

    Fluent( queue<Item>& q ): items( q ) {}
};

然后这样称呼:

Fluent( q ).push( 1 ).push( 2 ).push( 3 );

免责声明:编译器未触及任何代码。

玩得开心!

答案 2 :(得分:3)

这里只是为了好玩,这是一个小模板技巧,它提供了一种链接几乎所有方法的方法,忽略了返回值:

// The struct providing operator()(...) so that a call is simply
// chainer_t_instance(param_for_call1)(param_for_call2)(param_for_call3);
template <typename Class, typename Method>
struct chainer_t
{
    chainer_t(Class& instance, Method&& method) :
        _instance(instance),
        _method(method)
    {}

    chainer_t(chainer_t&& chainer) :
        _instance(chainer._instance),
        _method(chainer._method)
    {}

    // Avoid copy to avoid misunderstanding
    chainer_t(const chainer_t&) = delete;    
    chainer_t& operator=(const chainer_t&) = delete;

    // Operator () takes anything
    template <typename... Types>
    chainer_t& operator()(Types&&... types)
    {
        (_instance.*_method)(std::forward<Types>(types)...);
        return *this;
    }

protected:
    Class& _instance;
    Method& _method;
};

// Just to ease the writting
template <typename Class, typename Method>
chainer_t<Class, Method> chain(Class& instance, Method&& method)
{
    using chainer = chainer_t<Class, Method>;
    return chainer(instance, std::forward<Method>(method));
}

链接的电话只会是:

chain(my_instance, &my_class::add)(1)(2)(3)(4);

Live example

答案 3 :(得分:3)

auto repeat_call = [](auto&& f){
  return y_combinate(
    [f=decltype(f)(f)](auto&& self, auto&&...args)->decltype(self){
      f( decltype(args)(args)... );
      return decltype(self)(self);
    }
  );
};

y_combinate being a y combinator.

现在我们可以repeat_call( [&](int x){ q.push(x); } )(1)(0);

答案 4 :(得分:2)

如果您无法修改该类,您仍然可以使用逗号运算符:

#include<queue>
#include<iostream>

int main() {
    std::queue<int> q;
    (q.push(0), q).push(1);
    std::cout << q.size() << std::endl;
}

答案 5 :(得分:1)

这可能不是你想要的,但不要忘记,C ++不是一种基于行的语言(除了queue<int> q; q.push(0); q.push(1); 评论之外)。

因此,将多个简短的简单语句放在一行上是完全合理的。从而实现:

  

在一个队列上多次调用成员函数   线。

您只需要改变:

queue<int> q;
q.push(0); q.push(1);

分为:

auto &foo = a_really_long_name_for_a_queue;
foo.push(0); foo.push(1);

不,它不会删除键入q两次,但如果这是一个问题,我会怀疑你的问题更可能是名称过长的变量。假设情况总是记得你可以使用引用为变量提供更简单的本地句柄:

{{1}}