Подтвердить что ты не робот

Есть ли способ вызова нескольких функций на одном объекте с одной строкой?

Просто пытаясь привести в порядок программу и задавался вопросом, может ли кто-нибудь прокормить меня каким-то синтаксическим сахаром в отношении вызова функции-члена в одной очереди несколько раз в одной строке.

Например, изменение:

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 там дважды. Снова... синтаксический сахар:)

Цель здесь заключается не в инициализации, а в конденсировании количества раз, когда объект/контейнер воспитывается в блоке кода. Причина, по которой я ссылаюсь на очередь, состоит в том, что она динамическая.

4b9b3361

Ответ 1

Если у вас есть класс, который вы можете изменить, заставьте функцию вернуть ссылку себе:

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, так как очередь будет содержать 0 и 1 после двух нажатий.

Ответ 2

Вы всегда можете просто определить оболочку, например

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

Отказ от ответственности: ни один из кодов, затронутых компилятором.

Удачи!

Ответ 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);

Живой пример

Ответ 4

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 combinator.

Теперь мы можем repeat_call( [&](int x){ q.push(x); } )(1)(0);

Ответ 5

Если вы не можете изменить класс, вы все равно можете использовать оператор запятой:

#include<queue>
#include<iostream>

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

Ответ 6

Это не совсем то, что вы искали, но не забывайте, что С++ не является языком на основе строки (ну, кроме комментариев //).

Поэтому вполне разумно поместить несколько коротких простых операторов в одну строку. Таким образом, чтобы достичь:

вызов функции-члена в одной очереди несколько раз на одном и том же линия.

Вам нужно просто изменить:

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

В:

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

Нет, он не удаляет типизацию q дважды, но если это проблема, я бы заподозрил, что, скорее всего, ваша проблема - это переменные с чрезмерно длинными именами. Предполагая, что в этом случае всегда помните, что вы можете использовать ссылки, чтобы упростить локальные дескрипторы переменной:

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