【问题标题】:C++ perfect forwarding: why do we need forward() if we can use const_cast()?C++ 完美转发:如果可以使用 const_cast(),为什么还需要 forward()?
【发布时间】:2015-02-13 11:39:45
【问题描述】:

完美转发问题的常见描述表明,我们最好不要使用&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 做不同的事情怎么办?事实上,如果func 根据其参数的 const-ness 和 value 类别做不同的事情,你为什么还要用完美转发来调用它?
  • 如果有人真的传递了一个声明为const的对象呢?
  • 另外,您的解决方案不保留参数的值类别,即它不会将右值作为右值转发,一切都变成了非常量左值......这是最不完美的转发我'见过。这可以称为糟糕的转发。
  • Advantages of using forward 的可能重复项
  • 您基本上是在问“完美转发有什么用?”,隐藏在“如果我不想使用完美转发,我可以做完全不同的事情”的薄纱后面。 :)

标签: c++ c++11 perfect-forwarding const-cast


【解决方案1】:

你的解决方案真的,真的很糟糕。

您修改了参数的 const-ness,并且不保留参数的值类别(即左值或右值)。​​

基本上,您的包装器会完全改变参数,并始终调用 func 的相同重载,而不考虑原始参数的 const 限定符和值类别。

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

你与他们打交道,但你做错了。如果你的包装器是用右值调用的,它会将它们作为左值“转发”。如果func 关心左值和右值之间的区别,你的程序就会出现异常(如果不关心,为什么还要使用完美转发呢?)如果func 关心 const 和非常量参数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-23
    • 2017-07-08
    • 2010-12-26
    • 1970-01-01
    • 2014-01-04
    • 1970-01-01
    • 2014-09-06
    • 1970-01-01
    相关资源
    最近更新 更多