【问题标题】:How to perfectly forward `*this` object inside member function如何在成员函数中完美转发`*this`对象
【发布时间】:2022-03-05 06:55:09
【问题描述】:

是否可以在成员函数中完美转发*this 对象?如果是,那么我们该怎么做呢?如果不是,那为什么不呢?我们有什么替代方案才能达到同样的效果。

请查看下面的代码 sn-p 以更好地理解问题。

class Experiment {
public:
  double i, j;
  Experiment(double p_i = 0, double p_j = 0) : i(p_i), j(p_j) {}

  double sum() { return i + j + someConstant(); }

  double someConstant() && { return 10; }

  double someConstant() & { return 100; }
};

int main() {
  Experiment E(3, 5);
  std::cout << std::move(E).sum() << "\n";  // prints: 108
  std::cout << E.sum() << "\n";             // prints: 108
}

如果我们认为成员函数 double sum() 内的 *this 对象始终是左值或 xvalue(因此是 glvalue),则此输出似乎是预期的。请确认是否属实。

我们如何才能完美地将*this对象转发到double sum()成员函数内部的成员函数调用someConstant()

我尝试如下使用std::forward

double sum() {
    return i + j + std::forward<decltype(*this)>(*this).someConstant();
}

但这没有任何效果,double someConstant() &amp; 重载是总是被调用的。

【问题讨论】:

    标签: c++ c++11 overload-resolution


    【解决方案1】:

    如果不对 &amp;&amp;&amp; 限定符重载 sum,这在 C++11 中是不可能的。 (在这种情况下,您可以根据特定重载的限定符确定值类别。)

    *this 就像任何间接的结果一样,是一个左值,也是调用隐式成员函数调用的对象。

    这将在 C++23 中通过引入可以应用通常转发的显式对象参数来修复:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html

    【讨论】:

    • 非常感谢您的回答。您能否也确认一下:“如果我们认为成员函数 double sum() 中的 *this 对象始终是左值或 xvalue(因此是 glvalue),则此输出似乎是预期的。请确认这是否为真。”
    • @parth_07 我已经编辑了答案。
    【解决方案2】:

    有人会认为std::forward() 会保留左值引用,但它不会在非模板上下文中,如下例所示。

    call_f()&amp;call_f()&amp;&amp; 都调用 f()&amp;&amp;std::forward&lt;Experiment&gt;(*this) 在非模板函数中返回一个右值引用,无论参数的值类别如何。

    注意这与模板函数的工作方式有何不同,无论是否成员(我将成员函数设为静态,因为它接收“this 引用”作为显式参数)。两个前向左值都“正确”引用(最后 4 次调用)。

    #include<iostream>
    #include<utility>
    #include<string>
    
    struct Experiment 
    {
    public:
        std::string f()&& { return "f()&&"; }
        std::string f()&  { return "f()&"; }
    
        std::string call_f()&& { std::cout << "call_f()&& "; return std::forward<Experiment>(*this).f(); }
    
        // I need this function because it is not a template function
        std::string call_f()& { std::cout << "call_f()& "; return std::forward<Experiment>(*this).f(); }
    
        template<class T = Experiment>
        static std::string E_t_call_f(T&& t) { std::cout << "E_t_call_f(T&& t) "; return std::forward<T>(t).f(); }
    };
    
    template<class T> 
    std::string t_call_f(T&& t) { std::cout << "t_call_f(T&& t) "; return std::forward<T>(t).f(); }
    
    int main() 
    {
        Experiment E;
        std::cout << "E.f(): " << E.f() << '\n';
        std::cout << "move(E).f(): " << std::move(E).f() << '\n';
        std::cout << '\n';
        std::cout << "E.call_f(): " << E.call_f() << '\n';
        std::cout << "move(E).call_f(): " << std::move(E).call_f() << '\n';
        std::cout << '\n';
        std::cout << "t_call_f(E): " << t_call_f(E) << '\n';
        std::cout << "t_call_f(std::move(E)): " << t_call_f(std::move(E)) << '\n';
        std::cout << '\n';
        std::cout << "E::E_t_call_f(E): " << Experiment::E_t_call_f(E) << '\n';
        std::cout << "E::E_t_call_f(std::move(E)): " << Experiment::E_t_call_f(std::move(E)) << '\n';
    }
    
    

    在结果输出中,令人惊讶的是第三行:std::forward&lt;Experiment&gt;(*this) 的左值引用 *this 的类型是右值引用。

    E.f(): f()&
    move(E).f(): f()&&
    
    call_f()& E.call_f(): f()&&
    call_f()&& move(E).call_f(): f()&&
    
    t_call_f(T&& t) t_call_f(E): f()&
    t_call_f(T&& t) t_call_f(std::move(E)): f()&&
    
    E_t_call_f(T&& t) E::E_t_call_f(E): f()&
    E_t_call_f(T&& t) E::E_t_call_f(std::move(E)): f()&&
    

    【讨论】:

      猜你喜欢
      • 2012-01-24
      • 1970-01-01
      • 2022-11-20
      • 1970-01-01
      • 2018-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多