Есть ли способ вызова нескольких функций на одном объекте с одной строкой?
Просто пытаясь привести в порядок программу и задавался вопросом, может ли кто-нибудь прокормить меня каким-то синтаксическим сахаром в отношении вызова функции-члена в одной очереди несколько раз в одной строке.
Например, изменение:
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
там дважды. Снова... синтаксический сахар:)
Цель здесь заключается не в инициализации, а в конденсировании количества раз, когда объект/контейнер воспитывается в блоке кода. Причина, по которой я ссылаюсь на очередь, состоит в том, что она динамическая.
Ответы
Ответ 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);