mardi 24 février 2015

perfect forwarding failing for lvalues

I have a utility function which iterates over a tuple, and for each element, calls a function with that element, and finally calls another function with the result of all the tuple elements.


To better illustrate:



  • There is a tuple of various types tuple<Foo<int>, Bar<double>, Baz<char>>

  • Each type Foo, Bar and Baz have an implicit interface ClassT::data() which returns a reference to some internal member T&.

  • You have a function with the signature void (int, double, char)


The utility iterates over the tuple members, extracting the reference to the internal members, and calls the function with the required parameters.


Issue: perfect forwarding


I have tried to implement the utility using so-called "universal references" and perfect forwarding, thereby negating the need for multiple lvalue/rvalue and const/non-const overloads.


Whilst the parameter to the function is of type



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


I cannot bind lvalues to it. Why is this?


I have asked a similar question leading up to this here which has been solved; however since this is an unrelated issue, I believe this warrants a new question


Example on ideone: http://ift.tt/1JIKHFj



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

}

Aucun commentaire:

Enregistrer un commentaire