完美转发问题的常见描述表明我们最好不要使用&
和const&
的组合作为包装函数参数,因为在这种情况下我们必须编写多个函数涵盖函数参数的所有组合:
template <typename T1, typename T2>
void wrapper(T1& e1, T2& e2) { func(e1, e2); }
template <typename T1, typename T2>
void wrapper(const T1& e1, T2& e2) { func(e1, e2); }
template <typename T1, typename T2>
void wrapper(T1& e1, const T2& e2) { func(e1, e2); }
template <typename T1, typename T2>
void wrapper(const T1& e1, const T2& e2) { func(e1, e2); }
以下是该问题的经典解决方案:
template <typename T1, typename T2>
void wrapper(T1&& e1, T2&& e2) {
func(forward<T1>(e1), forward<T2>(e2));
}
template<class T>
T&& forward(typename std::remove_reference<T>::type& t) noexcept {
return static_cast<T&&>(t);
}
但为什么我们不能将const_cast
用于此目的?我们可以这样写:
template <typename T1, typename T2>
void wrapper(const T1& e1, const T2& e2)
{
T1& e1_ref = const_cast<T1&>(e1);
T2& e2_ref = const_cast<T2&>(e2);
func(e1_ref, e2_ref);
}
通过这种方式,我们不必编写多个函数,我们能够有效地处理左值和右值。那么为什么我们真的需要一个有点棘手的解决方案,使用参考折叠,模板参数推导和std::forward
?
答案 0 :(得分:9)
你的解决方案真的,真的坏。
您修改参数的常量,并且不保留参数的值类别(即左值或右值)。
基本上你的包装器完全改变了参数,总是调用func
的相同重载,而不考虑原始参数的const限定符和值类别。
通过这种方式,我们不必编写多个函数,我们能够有效地处理左值和右值。
你处理它们,但你做错了。如果用rvalues调用你的包装器,它就会转发&#34;转发&#34;他们作为左值。如果func
关心左值和右值之间的区别(如果它没有,为什么还要使用完美的转发?),如果func
关心的话,那么你的程序就会行为不端。 const和非const参数之间的区别。