web-dev-qa-db-ja.com

1行で同じオブジェクトの複数の関数を呼び出す方法はありますか?

プログラムを片付けようとしているだけで、同じ行で1つのキューのメンバー関数を複数回呼び出すことに関して、誰かが私に構文糖を与えることができるかどうか疑問に思っていました。

たとえば、次のように変更します。

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が2度存在しないようにしています。もう一度...構文糖:)

ここでの目標は、初期化することではなく、コードブロック内でオブジェクト/コンテナが呼び出される回数を圧縮することです。キューを参照する理由は、動的であるためです。

20
user6836841

変更可能なクラスがある場合は、関数がそれ自体への参照を返すようにします。

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 };

ただし、キューは2プッシュの後に0および1が含まれるため、これは明らかにPushでのみ機能します。

21
Rakete1111

あなたはいつでもラッパーを定義することができます

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 );

免責事項:コンパイラーが触れたコードはありません。

楽しんで!

10
_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_はYコンビネーターです。

これでrepeat_call( [&](int x){ q.Push(x); } )(1)(0);ができます

ここでおもしろいのは、戻り値を無視してほとんどすべてのメソッドをチェーンする方法を提供する小さなテンプレートトリックです。

// 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);

実例

3
Johan

クラスを変更できない場合でも、コンマ演算子を使用できます。

#include<queue>
#include<iostream>

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

これは正確にあなたが探していたものではないかもしれませんが、忘れないでください。C++は行ベースの言語ではありません(//コメントを除いて)。

したがって、1つの行に複数の短く単純なステートメントを置くことは完全に合理的です。したがって、達成するために:

同じ行で1つのキューのメンバー関数を複数回呼び出す。

変更するだけです。

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

に:

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

いいえ、qを2回入力しても削除されませんが、それが問題である場合は、名前が長すぎる変数が問題である可能性が高いと思います。これが当てはまる場合は常に、参照を使用して変数に簡単なローカルハンドルを与えることができることを覚えておいてください。

auto &foo = a_really_long_name_for_a_queue;
foo.Push(0); foo.Push(1);
1
Vality