【问题标题】:Can't bind lvalue to rvalue in member function but ok in global function无法在成员函数中将左值绑定到右值,但在全局函数中可以
【发布时间】:2021-04-07 09:58:25
【问题描述】:

在查看std::forward 的参考页面时,我发现了一些奇怪的东西。该示例将左值作为右值引用传递......但传递给全局函数......并且它编译并运行。我尝试了与成员函数相同的操作,但无法编译。是什么赋予了?如果不使用std::movestd::forward<T>,我希望这两个调用都会失败。

#include <iostream>

template <typename T>
void globalDoSomething(T &&data) {
    std::cout << "We're doing it!!" << std::endl;
}

template <typename T>
class A {
public:
    void doSomething(T &&data);
};

template <typename T>
void A<T>::doSomething(T &&data)
{
    std::cerr << "Ah, man. I won't compile." << std::endl;
}

template class A<int>;

int main()
{

    int b = 0;
    globalDoSomething(b);

    A<int> a;
    a.doSomething(b);

    return 0;
}

【问题讨论】:

    标签: c++ templates move-semantics rvalue-reference member-functions


    【解决方案1】:

    这是因为globalDoSomething的自动模板推导推断Tint&amp;

    如果你像对模板类的成员函数那样用globalDoSomething&lt;int&gt;(b);显式实例化模板函数,它也会编译失败。

    反之,如果你用A&lt;int&amp;&gt; a;实例化模板类,它会成功编译。

    【讨论】:

    • 啊啊啊,这很有意义!感谢您的快速答复。我已经在问题标签中添加了模板!
    • 换句话说,它实际上不是右值引用,而是所谓的“转发引用”(或旧说法中的“通用引用”)。虽然它的魔力是由非常普通的规则驱动的(正如 Patrick 指出的那样),但我们倾向于将带有模板 TT&amp;&amp; 视为它自己的特殊事物,因为这样做很方便。
    【解决方案2】:

    构建/添加@Patrick Roberts 的答案,来自template argument deduction

    如果 P 是对 cv 非限定模板参数的右值引用 (所谓的转发引用),以及对应的函数调用 参数是一个左值,就地使用对 A 的类型左值引用 的扣分

    简而言之,虽然b 是一个左值,但它确实将T 推断为int&amp;。并如示例所示

    template<class T>
    int f(T&& x) {                    // x is a forwarding reference
        return g(std::forward<T>(x)); // and so can be forwarded
    }
     
    int main() {
        int i;
        f(i); // argument is lvalue, calls f<int&>(int&), std::forward<int&>(x) is lvalue
    }
    

    为了测试这一点,你可以设置几个不同类型的重载函数并检查

    #include <iostream>
    #include <utility>
    
    
    void f(int&& x) {
        std::cout << "rvalue reference overload f("  ")\n";
    }
    
    void f(const int& x) {
        std::cout << "lvalue reference to const overload f("  ")\n";
    }
    
    void f(int& x) {
        std::cout << "lvalue reference overload f("  ")\n";
    }
    
    template <typename T>
    void globalDoSomething(T &&data) {
        f(data);
    }
    
    int main()
    {
        int b = 0;
        globalDoSomething(b); 
        return 0;
    }
    

    输出是

    左值引用重载 f()

    【讨论】:

    • 通过在globalDoSomething() 的主体中添加static_assert(std::is_same_v&lt;T, int&amp;&gt;); 来进行更简单的测试,无需定义一堆重载函数。
    • @PatrickRoberts 我只是粘贴了reference declaration 示例:)
    猜你喜欢
    • 1970-01-01
    • 2013-03-13
    • 1970-01-01
    • 2020-12-03
    • 2017-08-04
    • 2015-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多