web-dev-qa-db-ja.com

cpp.reactライブラリのC ++ソースコードの奇妙な「-> * []」式

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)ですか?どういう意味ですか?

55
Philipp Claßen

リンク先のページで_->*_が表示される唯一の例は次のとおりです。

_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()によって返される型はすべてオーバーロードします ポインタからメンバーへの演算子 _->*_、そしてそのオーバーロードされた演算子の2番目の引数は関数オブジェクトです。つまり、ラムダ式です。

この例について:

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

widthheightdepthの型が何であれ、コンマ演算子をオーバーロードすると、結果はMakeVarが生成するものと同じ型になります。または_->*_をオーバーロードする別のタイプ。残りは最初の例と同じです。

42
Praetorian

@ Praetorianの答え 正解です。 This は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);
}
_

ご覧のとおり、シグナル(D::MakeVar(0))とファンクター(ラムダ)を受け取るフリー関数_operator->*_がオーバーロードされています

2つの信号を受け取る無料の関数_operator,_

17
Bryan Chen

(著者はこちら)

まず、プラエトリアニの答えは正しいですが、少し詳しく説明したいと思います。

このライブラリはまだ非常に実験的であり、私はまだドキュメントに取り組んでいることに注意してください。上記のドキュメントの現在の状態は、特に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と同じではありません。ボリュームは関数オブジェクトではありません。特に、Value()を呼び出してもボリュームは再計算されず、依存するシグナルの1つが変更されたときにボリュームが再計算され、結果が保存され、Value()がそれを返します。したがって、これは基本的にプッシュベースの変更伝播であり、いくつかの追加機能があります(冗長な更新、グリッチ、オプションの暗黙的な並列化はありません)。

問題は、MakeSignalが一時的なシグナルやラムダと混合されると混乱することです。

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

これに関する問題は、他の人が指摘しているように、初めてそれを見る人は一体何が起こっているのかわからないということです。

一方、このライブラリを使用する場合、シグナルを関数に接続することは非常に一般的なタスクです。それが何をするかを理解すると、-> *バージョンはより簡潔になり、データフローグラフ(幅と高さから一時領域へのエッジ、領域と深さからボリュームへのエッジ)を視覚化します。

10
user3641432