56

这是我在cpp.react 库的文档中找到的 C++ 片段:

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

这是有效的 C++11(或 C++14)吗?这是什么意思?

4

3 回答 3

42

我看到的链接页面上的唯一示例->*是:

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()这是我的猜测 -重载指向成员运算符 的指针返回的任何类型->*,并且该重载运算符的第二个参数是函数对象,即 lambda 表达式。

至于这个例子:

auto volume = (width,height,depth) ->* [] (int w, int h, int d) {
    return w * h * d;
};

我猜任何类型width, height&depth是重载逗号运算符,结果产生的类型与产生的类型相同MakeVar,或者重载的另一种类型->*。其余与第一个示例相同。

于 2014-05-12T21:41:38.770 回答
17

@Praetorian 的回答是正确的。是来自 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)) 和函子 (lambda) 的自由函数

operator,和需要两个信号的自由函数

于 2014-05-12T22:02:43.317 回答
10

(作者在这里)

首先,Praetorians 的回答是正确的,但我想详细说明一下。

请注意,这个库仍然是非常实验性的,我仍在处理文档。可以在 wiki 中找到所述文档的当前状态,特别是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 不同。volume 不是函数对象。特别是,当您调用 Value() 时,不会重新计算交易量,而是在其相关信号之一发生变化时重新计算交易量,结果被保存,并且 Value() 将其返回。所以它本质上是基于推送的更改传播,带有一些额外的功能(没有冗余更新、没有故障、可选的隐式并行化)。

问题是 MakeSignal 在与临时信号和 lambda 混合时会变得混乱:

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

正如其他人所指出的那样,问题在于任何第一次看到它的人都不知道到底发生了什么。

另一方面,在使用这个库时,将信号连接到函数应该是一项非常常见的任务。一旦你知道它做了什么, ->* 版本会更加简洁,它可以可视化数据流图(从宽度和高度到临时区域的边缘,从区域和深度到体积的边缘)。

于 2014-05-15T16:57:27.843 回答