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()ポインター operator ->*をオーバーロードし、そのオーバーロードされた演算子の 2 番目の引数は関数オブジェクト、つまりラムダ式です。

この例については:

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

私はwidthheight&depthが何であれ、コンマ演算子をオーバーロードし、結果が生成されるものと同じ型MakeVar、またはオーバーロードする別の型を生成すると推測しています->*。あとは最初の例と同じです。

于 2014-05-12T21:41:38.770 に答える
17

@プレトリアンの答えは正しいです。これは 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,2つの信号を取る無料の関数

于 2014-05-12T22:02:43.317 に答える
10

(作者はこちら)

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

このライブラリはまだ非常に実験的なものであり、私はまだドキュメントに取り組んでいることに注意してください。上記のドキュメントの現在の状態は 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() を呼び出しても再計算されず、依存するシグナルの 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; };

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

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

于 2014-05-15T16:57:27.843 に答える