【问题标题】:What kind of problems for not forwarding universal reference?不转发通用引用会出现什么样的问题?
【发布时间】:2014-04-25 06:54:04
【问题描述】:

据我所知,在C++11中,通用引用应该总是和std::forward一起使用,但是我不确定如果不使用std::forward会出现什么样的问题。

template <T>
void f(T&& x);
{
    // What if x is used without std::forward<T>(x) ?
}

您能否提供一些在这种情况下可能出现的问题的说明?

【问题讨论】:

    标签: c++ c++11 perfect-forwarding universal-reference


    【解决方案1】:

    没有这样的规则总是std::forward通用引用一起使用。相反,在通用引用的函数中到处使用std::forward 可能很危险。看看下面的例子:

    template <typename T>
    auto make_pair(T&& t)
    {
        return std::make_tuple(std::forward<T>(t), std::forward<T>(t)); // BAD
    }
    

    如果你用make_pair(std::string{"foobar"})调用这个函数,结果是反直觉的,因为你从同一个对象移动了两次。


    更新:这是另一个示例,说明在没有完美转发的情况下使用通用引用真的很有意义:

    template <typename Range, typename Action>
    void foreach(Range&& range, Action&& action)
    {
        using std::begin;
        using std::end;
        for (auto p = begin(range), q = end(range); p != q; ++p) {
            action(*p);
        }
    }
    
    • range 是一个通用引用 很好,这样调用者就可以将 foreach 与一个临时容器和一个动作一起使用,这就是调用元素上的 非常量 成员函数。
    • action 是一个通用引用 很好,这样调用者就可以将可变 lambda 表达式 作为操作传递。
    • std::forward 用于rangeaction 是错误的。

    【讨论】:

    • 很好,但没有明确回答“如果不使用 std::forward 会出现什么样的问题?”的问题
    • +1,但是为什么将std::forward 用于action 是错误的?我刚读到这个:Why use a perfectly forwarded value?,如果不保留 r 值,您似乎可能会放弃优化机会。
    • @Snps:与std::apply相比,上面的代码包含一个循环。根据Action 的类型,std::forward&lt;Action&gt;(action) 可以与std::move(action) 相同。这意味着使用std::forward,您可能会在第一次迭代中更改函数对象action,并导致所有剩余迭代出现奇怪的行为。
    • @nosid Duh,循环(我怎么会错过)。现在有道理了,感谢您澄清!
    【解决方案2】:

    假设f 是这样调用的:

    f(someType{});
    

    如果f 的主体对x 执行某种操作

    foo(x);
    

    foo 有两个重载

    void foo(someType const&);
    
    void foo(someType&&);
    

    不使用std::forward,因为x 是一个左值,所以会调用以下重载

    void foo(someType const&);
    

    这可能会让您付出潜在的优化成本。

    使用

    foo(std::forward<T>(x));
    

    确保选择了正确的 foo 重载。

    【讨论】:

      【解决方案3】:

      有名字的东西是左值。这意味着在函数体中,t 是一个左值。通用引用最终是左值引用还是右值引用并不重要。如果它被命名,它就是一个左值。

      如果您因此直接传递该参数,您将传递一个左值。

      在您只想将不透明的参数传递给另一个函数的情况下,您希望完全按原样传递它。如果它是一个左值,你想把它作为一个左值传递;如果它是一个右值,你想传递一个右值。如前所述,直接将其作为左值传递总是forward 具有将其作为正确的值传递所需的魔力,

      始终传递左值的代码可能会从性能角度受到影响,但我想说,在考虑这个特定问题时,您可以忽略这一点。不总是传递左值有一个更紧迫的问题:虽然复制某些类型可能很昂贵,但某些其他类型根本无法复制,例如std::unique_ptr。对于那些在转发时保持右值性的类型不是性能问题,而是让您的代码甚至可以编译。

      【讨论】:

        【解决方案4】:

        您不应多次转发变量。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-07-17
          • 2011-01-23
          • 2021-09-17
          • 1970-01-01
          相关资源
          最近更新 更多