左值的完美转发失败
perfect forwarding failing for lvalues
我有一个实用函数,它遍历一个元组,并为每个元素调用一个带有该元素的函数,最后用所有元组元素的结果调用另一个函数。
为了更好地说明:
- 存在各种类型的元组
tuple<Foo<int>, Bar<double>, Baz<char>>
- 每种类型
Foo
、Bar
和 Baz
都有一个隐式接口 ClassT::data()
,其中 returns 对某些内部成员的引用 T&
.
- 您有一个带有签名的函数
void (int, double, char)
实用程序遍历元组成员,提取对内部成员的引用,并使用所需参数调用函数。
问题:完美转发
我尝试使用所谓的 "universal references" 和完美转发来实现该实用程序,从而不需要多个 lvalue/rvalue 和 const/non-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;
}
问题出在您的 apply
函数上 - 它需要 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
而不是代码中的 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));
}
template<typename Func, typename ForEachFunc, typename... Args>
static auto apply(Func&& func, ForEachFunc&& forEachFunc, std::tuple<Args...>&& args)
第三个参数是右值tuple
,不是转发引用的元组,也不是对元组的转发引用。
通用引用(目前在开发中的 C++1z 标准中称为 "forwarding references")通常需要推导类型。他们确实要求应用 &&
的类型可以是引用类型或非引用类型。 std::tuple<?>
始终是非引用类型,因此 &&
使其成为右值引用,而不是转发引用,如 std::tuple<?>&&
.
您从 Tuple&&
切换到 std::tuple<?>&&
似乎没有任何原因,只是为了计算 Args...
的数量。如果我是对的,那么 std::tuple_size<std::decay_t<Tuple>>{}
等于 sizeof...(Args)
.
我有一个实用函数,它遍历一个元组,并为每个元素调用一个带有该元素的函数,最后用所有元组元素的结果调用另一个函数。
为了更好地说明:
- 存在各种类型的元组
tuple<Foo<int>, Bar<double>, Baz<char>>
- 每种类型
Foo
、Bar
和Baz
都有一个隐式接口ClassT::data()
,其中 returns 对某些内部成员的引用T&
. - 您有一个带有签名的函数
void (int, double, char)
实用程序遍历元组成员,提取对内部成员的引用,并使用所需参数调用函数。
问题:完美转发
我尝试使用所谓的 "universal references" 和完美转发来实现该实用程序,从而不需要多个 lvalue/rvalue 和 const/non-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;
}
问题出在您的 apply
函数上 - 它需要 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
而不是代码中的 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));
}
template<typename Func, typename ForEachFunc, typename... Args>
static auto apply(Func&& func, ForEachFunc&& forEachFunc, std::tuple<Args...>&& args)
第三个参数是右值tuple
,不是转发引用的元组,也不是对元组的转发引用。
通用引用(目前在开发中的 C++1z 标准中称为 "forwarding references")通常需要推导类型。他们确实要求应用 &&
的类型可以是引用类型或非引用类型。 std::tuple<?>
始终是非引用类型,因此 &&
使其成为右值引用,而不是转发引用,如 std::tuple<?>&&
.
您从 Tuple&&
切换到 std::tuple<?>&&
似乎没有任何原因,只是为了计算 Args...
的数量。如果我是对的,那么 std::tuple_size<std::decay_t<Tuple>>{}
等于 sizeof...(Args)
.