Strange "-> * []" в исходном коде С++ библиотеки cpp.react
Вот фрагмент С++, который я нашел в документации библиотеки cpp.react:
auto in = D::MakeVar(0);
auto op1 = in ->* [] (int in)
{
int result = in /* Costly operation #1 */;
return result;
};
Я никогда не видел обозначения ->* []
. Во-первых, я думал, что это просто опечатка, но я нашел такое выражение в исходном коде:
auto volume = (width,height,depth) ->* [] (int w, int h, int d) {
return w * h * d;
};
Является ли это допустимым С++ 11 (или С++ 14)? Что это значит?
Ответы
Ответ 1
Единственный пример на связанной странице, где я вижу ->*
, это:
auto in = D::MakeVar(0);
auto op1 = in ->* [] (int in)
{
int result = in /* Costly operation #1 */;
return result;
};
auto op2 = in ->* [] (int in)
{
int result = in /* Costly operation #2 */;
return result;
};
Здесь моя догадка - любой тип, возвращаемый D::MakeVar()
, перегружает оператор указатель на член ->*
, а второй аргумент для этого перегруженного оператора объект функции, т.е. лямбда-выражение.
Как в этом примере:
auto volume = (width,height,depth) ->* [] (int w, int h, int d) {
return w * h * d;
};
Я предполагаю, что любые типы width
, height
и depth
перегружают оператор запятой, а результат дает тот же тип, что и для MakeVar
, или другой тип, который перегружает ->*
. Остальное - это то же, что и в первом примере.
Ответ 2
@Praetorian answer является правильным. Это - код cpp.react
///////////////////////////////////////////////////////////////////////////////////////////////////
/// operator->* overload to connect inputs to a function and return the resulting node.
///////////////////////////////////////////////////////////////////////////////////////////////////
// Single input
template
<
typename D,
typename F,
template <typename D_, typename V_> class TSignal,
typename TValue,
class = std::enable_if<
IsSignal<TSignal<D,TValue>>::value>::type
>
auto operator->*(const TSignal<D,TValue>& inputNode, F&& func)
-> Signal<D, typename std::result_of<F(TValue)>::type>
{
return D::MakeSignal(std::forward<F>(func), inputNode);
}
// Multiple inputs
template
<
typename D,
typename F,
typename ... TSignals
>
auto operator->*(const InputPack<D,TSignals ...>& inputPack, F&& func)
-> Signal<D, typename std::result_of<F(TSignals ...)>::type>
{
return apply(
REACT_IMPL::ApplyHelper<D, F&&, TSignals ...>::MakeSignal,
std::tuple_cat(std::forward_as_tuple(std::forward<F>(func)), inputPack.Data));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
/// Comma operator overload to create input pack from 2 signals.
///////////////////////////////////////////////////////////////////////////////////////////////////
template
<
typename D,
typename TLeftVal,
typename TRightVal
>
auto operator,(const Signal<D,TLeftVal>& a, const Signal<D,TRightVal>& b)
-> InputPack<D,TLeftVal, TRightVal>
{
return InputPack<D, TLeftVal, TRightVal>(a, b);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
/// Comma operator overload to append node to existing input pack.
///////////////////////////////////////////////////////////////////////////////////////////////////
template
<
typename D,
typename ... TCurValues,
typename TAppendValue
>
auto operator,(const InputPack<D, TCurValues ...>& cur, const Signal<D,TAppendValue>& append)
-> InputPack<D,TCurValues ... , TAppendValue>
{
return InputPack<D, TCurValues ... , TAppendValue>(cur, append);
}
поскольку вы можете видеть, что он перегружает свободную функцию operator->*
, которая принимает сигнал (D::MakeVar(0)
) и функтор (лямбда)
и свободная функция operator,
, которая принимает два сигнала
Ответ 3
(Автор здесь)
Прежде всего, преторианцы отвечают правильно, но я хотел бы немного уточнить.
Обратите внимание, что эта библиотека все еще очень экспериментальная, и я все еще работаю над документацией.
Текущее состояние указанной документации можно найти в вики, в частности https://github.com/schlangster/cpp.react/wiki/User-Guide-%7C-Signals связано с вопросом.
Здесь приведен более подробный пример:
int calcVolume(int w, int h, int d) { return w*h*d; }
D::VarSignalT<int> width = D::MakeVar(1);
D::VarSignalT<int> height = D::MakeVar(2);
D::VarSignalT<int> depth = D::MakeVar(3);
D::SignalT<int> volume = MakeSignal(&calcVolume, width, height, depth);
Observe(volume, [] (int v) {
printf("volume changed to %d\n", v);
});
width.Set(10); // => volume changed to 60.
printf("volume: %d\n", volume.Value()); // short: volume()
Это тип привязки (привязка сигналов как вход функции), но это НЕ то же самое, что обратный std:: bind. том не является функциональным объектом.
В частности, объем не пересчитывается при вызове Value(), он пересчитывается, когда изменяется один из его зависимых сигналов, результат сохраняется, а Value() возвращает его.
Таким образом, он существенно продвигает распространение изменений на основе с некоторыми дополнительными функциями (без избыточных обновлений, без сбоев, необязательной неявной распараллеливания).
Проблема заключается в том, что MakeSignal запутывается при смешивании с временными сигналами и lambdas:
// First create a temporary area signal, then use it as an argument for the volume signal
D::SignalT<int> volume = MakeSignal(
[] (int a, int d) { return a * d; },
MakeSignal(
[] (int w, int h) { return w * h; },
width, height),
depth);
Никто не хочет читать такие вещи, верно? По крайней мере, я не хочу.
Итак, есть альтернативный синтаксис, который перемещает зависимости слева, обернутые SignalList.
// Note: Not sure if I have already pushed this variant yet
D::SignalT<int> volume =
MakeSignalList(
MakeSignalList(width, height).Bind([] (int w, int h) { return w * h; }),
depth
).Bind([] (int a, int d) { return a * d; });
И, наконец, со злой запятой и → * перегрузками:
D::SignalT<int> volume =
(
(width, height) ->* [] (int w, int h) { return w * h; },
depth
)
->* [] (int area, int d) { return a * d; };
Проблема с этим, как отмечали другие, заключается в том, что каждый, кто впервые видит это, не знает, что происходит.
С другой стороны, при использовании этой библиотеки при подключении сигналов к функциям должна быть очень распространенная задача.
Как только вы знаете, что он делает, версия → * более кратка и визуализирует график потока данных (края от ширины и высоты до временной области, края от области и глубины до тома).