【问题标题】:Perfect forwarding a functor完美转发函子
【发布时间】:2016-02-11 20:30:22
【问题描述】:

我想知道使用完美转发函子的正确方法是什么?这是两个代码sn-p。哪一个是最好的,如果都不是,最好的形式是什么?

template<typename T, typename... Args>
void callMe(T&& func, Args&&... args) {
    func(std::forward<Args>(args)...);
}

或者

template<typename T, typename... Args>
void callMe(T&& func, Args&&... args) {
    std::forward<T>(func)(std::forward<Args>(args)...);
}

编辑:

它会影响重载解决方案吗?如果funcoperator() 具有&amp;&amp;const &amp; 的引用限定符,我应该使用后一个版本吗?我应该关心我调用哪个重载吗?

谢谢!

【问题讨论】:

  • 如果函数调用运算符可能有引用限定符,则后者是正确的
  • @PiotrSkotnicki,你能举例说明你的意思吗?
  • 感谢您指出这一点。编辑了我的问题。

标签: c++ templates functor perfect-forwarding


【解决方案1】:

由于 ref-qualified operator() 存在,第一个版本可能会做错事。考虑:

struct C {
    void operator()() && { std::cout << "rval\n"; }
    void operator()() const & { std::cout << "lval\n"; }
};

callMe(C{});

我给你一个右值 - 并且希望看到 "rval" - 但在第一个版本中,你总是将函数对象视为左值 - 所以我真的看到了 "lval"

所以正确的解决方案是第二个 - forwards func 也是如此。


在实践中,我不知道 ref 限定的成员函数实际发生的频率,所以前者可能没问题。

【讨论】:

  • 酷。我不知道存在 ref-qualification。
  • 我担心的是 lambda 的优化,其中右值 lambda 开始隐式地从其存储的内部状态(如局部变量)移动。在某些情况下,这可能会给您带来一些不错的性能提升,并且缺少前锋将会丢失。但可能不会。
  • @Yakk 我读到“用 C++ 正确编写通用代码可能是不可能的”
【解决方案2】:

完美转发在您要将一个对象(可能是一个右值)传递到某个其他函数并且您不确定它是一个右值还是一个左值的情况下很有用。在这种情况下,您只需使用 func,因此转发它没有任何好处或坏处。

请记住,std::forward 是一个条件std::move,它只是对 r 值引用的强制转换。

此外,只有当你转发的东西有可能调用复制或移动构造函数时,它才真正有用。在许多情况下,const T&amp; 就可以了。

编辑:

正如 berry 指出的那样,func 是否具有 ref-qualified operator() 确实。我从未见过或使用过 ref 限定方法(据我所知。这是极其罕见的。)更多信息请参见 Barry 的回答。

它也会读到这个:What is "rvalue reference for *this"?

struct C {
    void operator()() && { std::cout << "rval\n"; }
    void operator()() const & { std::cout << "lval\n"; }
};

callMe(C{});

【讨论】:

  • +1 同意。对于其他读者,我想指出,如果您只打算使用它,转发它并没有什么害处,因为std::forward 只是对 r 值引用 (&amp;&amp;) 或 l 值引用的类型转换(&amp;)。
  • 不同意“获得/伤害”。如果 T 具有 ref-qualified operator() 怎么办?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-05
  • 1970-01-01
  • 1970-01-01
  • 2015-09-07
  • 2020-09-12
相关资源
最近更新 更多