タプルを反復処理し、要素ごとにその要素で関数を呼び出し、最後にすべてのタプル要素の結果で別の関数を呼び出すユーティリティ関数があります。
わかりやすく説明すると、次のようになります。
- いろいろな種類のタプルがある
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;
}