完美转发问题的常见描述是我们最好不要使用 &
和 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
的有点棘手的解决方案?
最佳答案
你的解决方案真的,真的很糟糕。
您修改了参数的常量性,并且不保留参数的值类别(即左值或右值)。
基本上,您的包装器会完全改变参数并始终调用相同的 func
重载,而不考虑原始参数的 const 限定符和值类别。
In this way, we don't have to write multiple functions and we are able to efficiently deal with both lvalues and rvalues.
你和他们打交道,但你做错了。如果使用右值调用包装器,它会将它们“转发”为左值。如果 func
关心左值和右值之间的区别(如果不关心,为什么还要使用完美转发呢?)如果 func
关心 const 和非常量参数之间的区别。
关于C++完美转发: why do we need forward() if we can use const_cast()?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28498769/