C++14 遇到奇怪的 "use of deleted function" 错误

C++14 encounters strange "use of deleted function" error

下面是 g++7.3.1 -std=c++14 的简单代码段

#include<functional>
#include<iostream>
using namespace std;
struct S{
    int m_i = 2;
    auto& get2(){return ref(m_i);}
};
int main(){
    S s;
    s.get2()=4;
    cout<<s.m_i<<endl;
    return 0;
}

编译错误如下:

error: cannot bind non-const lvalue reference of type ‘std::reference_wrapper<int>&’ to an rvalue of type ‘std::reference_wrapper<int>’
    auto& get2(){return ref(m_i);}
                        ~~~^~~~~
In function ‘int main()’:
error: use of deleted function ‘std::reference_wrapper<_Tp>::reference_wrapper(_Tp&&) [with _Tp = int]’
    s.get2()=4;
            ^
In file included from /usr/include/c++/7/bits/std_function.h:44:0,
                from /usr/include/c++/7/functional:58,
                from xxx.cpp:1:
/usr/include/c++/7/bits/refwrap.h:338:7: note: declared here
    reference_wrapper(_Tp&&) = delete;
    ^~~~~~~~~~~~~~~~~

我知道,通过删除 "ref" 包装器,程序可以编译。我只想知道在这里使用 "ref" 有什么问题?

  1. 只要我想return一个引用,为什么这里加一个"ref"会出错?
  2. 我不明白为什么 g++ 将其视为 "use of deleted function",哪个函数被删除了?

std::ref 不仅仅是 return 一个引用,它 return 是一个 引用包装器 。具体来说,std::reference_wrapper<T>。所以当你写 return std::ref(m_i); 时,你不是 returning int&,而是 returning std::reference_wrapper<int> &,对本身包含的悬空对象的引用一个参考。

只是 return 成员原样;编译器会理解它是一个参考。

#include<functional>
#include<iostream>
//Don't use using namespace std;, it's bad practice

struct S{
    int m_i = 2;
    auto& get2(){return m_i;}
};
int main(){
    S s;
    s.get2()=4;
    std::cout<<s.m_i<<std::endl;
    return 0;
}

还可以考虑将 return 类型显式化,因为在这种情况下,您不会从使用 auto.

中获益良多
#include<functional>
#include<iostream>

struct S{
    int m_i = 2;
    int& get2(){return m_i;}
};
int main(){
    S s;
    s.get2()=4;
    std::cout<<s.m_i<<std::endl;
    return 0;
}

您共享的代码存在一些问题。

第一个问题是您 return 引用了一个对象,该对象在您的函数 return 时将不再存在。 std::ref return 一个 std::reference_wrapper 将超出范围。 std::reference_wrapper 是一个对象,它提供了引用类型的一些功能,同时仍然表现得像一个对象。它具有移动语义,因此正确的用法是 return 按值 std::reference_wrapper

#include<functional>
struct S {
    int m_i = 2;
    auto get2() { return std::ref(m_i); }
};

虽然正如其他人指出的那样,std::ref 这里并不是真正需要的。您可以直接 return 引用 m_i。如果用户碰巧确实需要 std::reference_wrapper 对应 int,他们可以参考您提供的 m_i 自己调用 std::ref

struct S {
    int m_i = 2;
    int & get2() { return  m_i; }
};

但这不是您的代码的唯一问题。假设您修复了第一个问题并且 return 您的 std::reference_wrapper 按值,您仍然在滥用 std::reference_wrapper::operator=。该运算符不会为引用的对象分配新值。相反,它重新绑定引用以引用不同的实例。正确的用法是使用 get()std::reference_wrapper :

中获取适当的可分配 int&
#include<functional>
#include<iostream>
struct S {
    int m_i = 2;
    auto get2() { return std::ref(m_i); }
};

int main() {
    S s;
    s.get2().get() = 4;
    std::cout << s.m_i << std::endl;
    return 0;
}

但是错误信息呢?编译器看到 std::reference_wrapper 存在的唯一 operator= 是另一个 std::reference_wrapper。因此它试图通过尝试使用采用单个参数的构造函数来隐式地将 4 转换为 std::reference_wrapper 。但是,不可能将 std::reference_wrapper 创建为像 4 这样的文字(或一般的右值)。它需要一个合适的左值来引用。这可以通过声明一个构造函数来防止,该构造函数接受对包装类型的右值引用(在本例中为 int)并将其删除。尝试使用该构造函数将产生您所看到的错误。删除的函数是std::reference_wrapper<int>::reference_wrapper<int>(int&&),由编译器引入的转换调用