C++ 14遇到奇怪的"使用已删除的函数"错误

Tro*_*yvs 0 c++ reference auto c++14

我在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;
}
Run Code Online (Sandbox Code Playgroud)

它编译错误如下:

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;
    ^~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

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

  1. 只要我想返回一个引用,为什么在这里添加"ref"会导致错误?
  2. 我不明白为什么g ++将其视为"使用已删除的函数",哪个函数被删除?

Xir*_*ema 5

std::ref它不仅仅返回一个引用,它返回一个Reference Wrapper.具体来说,std::reference_wrapper<T>.因此,当你写作时return std::ref(m_i);,你没有返回int&,你正在返回std::reference_wrapper<int> &,这是对一个本身包含引用的悬空对象的引用.

只需按原样退回会员; 编译器会理解它是一个参考.

#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;
}
Run Code Online (Sandbox Code Playgroud)

另外考虑使返回类型显式化,因为这不是一个你从使用中获益很多的情况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;
}
Run Code Online (Sandbox Code Playgroud)


Fra*_*eux 5

您共享的代码存在许多问题.

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

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

虽然正如其他人指出的那样,std::ref但这里并不需要.您可以直接返回引用m_i.如果用户碰巧真的需要一个std::reference_wrapper,那么int他们可以std::ref使用m_i您提供的引用来调用自己.

struct S {
    int m_i = 2;
    int & get2() { return  m_i; }
};
Run Code Online (Sandbox Code Playgroud)

但这不是您的代码唯一的问题.假设你修复了第一个问题并返回了你std::reference_wrapper的值,你仍然在滥用std::reference_wrapper::operator=.该运算符不会为引用的对象分配新值.相反,它重新引用引用以引用不同的实例.正确的用法是用来从以下方面get()获得适当的可分配:int&std::reference_wrapper

#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;
}
Run Code Online (Sandbox Code Playgroud)

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