0

タプルを反復処理し、要素ごとにその要素で関数を呼び出し、最後にすべてのタプル要素の結果で別の関数を呼び出すユーティリティ関数があります。

わかりやすく説明すると、次のようになります。

  • いろいろな種類のタプルがあるtuple<Foo<int>, Bar<double>, Baz<char>>
  • 各型FooにはBar、何らかの内部メンバーへの参照を返すBaz暗黙のインターフェースがあります。ClassT::data()T&
  • 署名付きの関数がありますvoid (int, double, char)

このユーティリティは、タプル メンバーを反復処理して内部メンバーへの参照を抽出し、必要なパラメーターを指定して関数を呼び出します。

問題: 完全転送

いわゆる「ユニバーサル参照」と完全な転送を使用してユーティリティを実装しようとしました。これにより、複数の左辺値/右辺値および const/非 const オーバーロードの必要性がなくなりました。

関数へのパラメーターは型ですが、

template<typename Tuple>
auto invoke(Tuple&& tuple)

左辺値をそれにバインドできません。どうしてこれなの?

これに至るまで同様の質問をここで行いましたが、解決されました。ただし、これは無関係の問題であるため、これには新しい質問が必要だと思います

ideone の例: https://ideone.com/lO5JOB

#include <tuple>
#include <iostream>

// sequence

template<size_t...>
struct Sequence
{ };

template<size_t N, size_t... Seq>
struct GenerateSequence : GenerateSequence<N - 1, N - 1, Seq...>
{ };

template<size_t... Seq>
struct GenerateSequence<0, Seq...>
{
    using type = Sequence<Seq...>;
};

// invoke tuple

struct TupleForEachInvoker
{
    template<typename Func, typename ForEachFunc, typename Tuple, size_t... Seq>
    static auto invoke(Func&& func, ForEachFunc&& forEachFunc, Tuple&& tuple, Sequence<Seq...>)
        -> decltype(func(forEachFunc(std::get<Seq>(std::forward<Tuple>(tuple)))...))
    {
        return func(forEachFunc(std::get<Seq>(std::forward<Tuple>(tuple)))...);
    }

    template<typename Func, typename ForEachFunc, typename... Args>
    static auto apply(Func&& func, ForEachFunc&& forEachFunc, std::tuple<Args...>&& args)
        -> decltype(invoke(std::forward<Func>(func),
                           std::forward<ForEachFunc>(forEachFunc),
                           std::forward<std::tuple<Args...>>(args),
                           typename GenerateSequence<sizeof...(Args)>::type()))
    {
        return invoke(std::forward<Func>(func),
                      std::forward<ForEachFunc>(forEachFunc),
                      std::forward<std::tuple<Args...>>(args),
                      typename GenerateSequence<sizeof...(Args)>::type());
    }
};

template<typename Func, typename ForEachFunc, typename Tuple>
inline auto invokeWithMemberFromAll(Func&& func, ForEachFunc&& forEachFunc, Tuple&& tuple)
    -> decltype(TupleForEachInvoker::apply(std::forward<Func>(func),
                                           std::forward<ForEachFunc>(forEachFunc),
                                           std::forward<Tuple>(tuple)))
{
    return TupleForEachInvoker::apply(std::forward<Func>(func),
                                      std::forward<ForEachFunc>(forEachFunc),
                                      std::forward<Tuple>(tuple));
}

// exemplar

template<typename T>
struct Foo
{
    T& data() { return _val; }
    T _val;
};

struct Extract
{
    template<typename T>
    T& operator() (Foo<T>& f) { return f.data(); }
};

int main()
{
    Foo<int>         i { 5 };
    Foo<double>      d { 6. };
    Foo<const char*> s { "hello world" };

    auto cb = [](int& i, const double& d, const char* s)
        {
            std::cout << "i=" << i << ", d=" << d << ", s=" << s << std::endl;

            i += 2;
        };


    // rvalue reference to tuple
    invokeWithMemberFromAll(cb, Extract{}, std::tie(i, d, s));

    std::cout << i.data() << std::endl;

    // lvalue reference to tuple - fails
    auto tuple = std::tie(i, d, s);
    invokeWithMemberFromAll(cb, Extract{}, tuple);

    std::cout << i.data() << std::endl;

}
4

2 に答える 2

5
template<typename Func, typename ForEachFunc, typename... Args>
static auto apply(Func&& func, ForEachFunc&& forEachFunc, std::tuple<Args...>&& args)

3 番目の引数は rvaluetupleであり、転送参照のタプルでも、タプルへの転送参照でもありません。

ユニバーサル参照 (開発中の C++1z 標準では現在「転送参照」と呼ばれています) は通常、推定型を必要とします。&&が適用される型が参照型または非参照型のいずれかである必要があります。Astd::tuple<?>は常に非参照型であるため、のよう&&に配置すると、転送参照ではなく右辺値参照になりstd::tuple<?>&&ます。

の数を数える以外の理由でからTuple&&に切り替えたようです。私が正しければ、 に等しい。std::tuple<?>&&Args...std::tuple_size<std::decay_t<Tuple>>{}sizeof...(Args)

于 2015-02-24T22:11:26.933 に答える
2

問題はあなたのapply関数にあります - それには of が必要rvalueですstd::tuple<Args...>:

template<typename Func, typename ForEachFunc, typename... Args>
    static auto apply(Func&& func, ForEachFunc&& forEachFunc, std::tuple<Args...>&& args)

コードstd::tuple_sizeの代わりに使用できる修正の 1 つを次に示します。sizeof...(Args)

struct TupleForEachInvoker
{
    template<typename Func, typename ForEachFunc, typename Tuple, size_t... Seq>
    static auto invoke(Func&& func, ForEachFunc&& forEachFunc, Tuple&& tuple, Sequence<Seq...>)
        -> decltype(func(forEachFunc(std::get<Seq>(std::forward<Tuple>(tuple)))...))
    {
        return func(forEachFunc(std::get<Seq>(std::forward<Tuple>(tuple)))...);
    }

    template<typename Func, typename ForEachFunc, typename Tuple>
    static auto apply(Func&& func, ForEachFunc&& forEachFunc, Tuple&& args)
        -> decltype(invoke(std::forward<Func>(func),
                           std::forward<ForEachFunc>(forEachFunc),
                           std::forward<Tuple>(args),
                           typename GenerateSequence<std::tuple_size<std::decay_t<Tuple>>::value>::type()))
    {
        return invoke(std::forward<Func>(func),
                      std::forward<ForEachFunc>(forEachFunc),
                      std::forward<Tuple>(args),
                      typename GenerateSequence<std::tuple_size<std::decay_t<Tuple>>::value>::type());
    }
};

template<typename Func, typename ForEachFunc, typename Tuple>
inline auto invokeWithMemberFromAll(Func&& func, ForEachFunc&& forEachFunc, Tuple&& tuple)
    -> decltype(TupleForEachInvoker::apply(std::forward<Func>(func),
                                           std::forward<ForEachFunc>(forEachFunc),
                                           std::forward<Tuple>(tuple)))
{
    return TupleForEachInvoker::apply(std::forward<Func>(func),
                                      std::forward<ForEachFunc>(forEachFunc),
                                      std::forward<Tuple>(tuple));
}

ライブデモ

于 2015-02-24T22:04:22.753 に答える