C++完美转发:如果我们可以使用const_cast(),为什么我们需要forward()?

und*_*ind 1 c++ const-cast perfect-forwarding c++11

完美转发问题的常见描述表明我们最好不要使用&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); }
Run Code Online (Sandbox Code Playgroud)

以下是该问题的经典解决方案:

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);
}
Run Code Online (Sandbox Code Playgroud)

但为什么我们不能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);
}
Run Code Online (Sandbox Code Playgroud)

通过这种方式,我们不必编写多个函数,并且能够有效地处理左值和右值.那么为什么我们真的需要一个有点棘手的解决方案,使用参考折叠,模板参数推导和std::forward

Jon*_*ely 9

你的解决方案非常非常糟糕.

您修改参数的常量,并且不保留参数的值类别(即左值或右值).

基本上你的包装器完全改变了参数,总是调用相同的重载,func而不考虑原始参数的const限定符和值类别.

通过这种方式,我们不必编写多个函数,并且能够有效地处理左值和右值.

你处理它们,但你做错了.如果使用rvalues调用包装器,它会将它们"转发"为lvalues.如果func关心左值和左值之间的区别,你的程序会行为不端(如果没有,为什么还要使用完美的转发?)如果func关心const和非const参数之间的区别,它也会行为不端.