【问题标题】:C++14 encounters strange "use of deleted function" errorC++14 遇到奇怪的“使用已删除函数”错误
【发布时间】:2018-09-13 17:09:14
【问题描述】:

我在下面有一个简单的 g++7.3.1 -std=c++14 的 sn-p

#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. 只要我想返回一个引用,为什么在这里添加一个“引用”会导致错误?
  2. 我不明白为什么 g++ 将其视为“使用已删除函数”,正在删除哪个函数?

【问题讨论】:

  • 错误信息有什么不清楚的地方?
  • get2 返回一个引用。这是一个问题,因为 1)它将是一个悬空引用(它引用的实例在函数末尾超出范围)和 2)您不能将非 const 引用绑定到临时对象,例如从ref(m_i); 获得的那个。只需按值返回。
  • @FrançoisAndrieux 为什么悬空?
  • 您是否正在尝试解决一些真正的问题?你的例子似乎只是为了重现错误,否则我不明白你为什么写这个
  • @Rakete1111 该函数返回对临时std::reference_wrapper的引用。

标签: c++ reference c++14 auto


【解决方案1】:

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

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

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

尽管正如其他人指出的那样,std::ref 在这里并不真正需要。您可以直接返回对m_i 的引用。如果用户碰巧实际上需要一个std::reference_wrapper 来获得那个int,他们可以使用您提供的m_i 引用自己调用std::ref

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

但这不是您的代码的唯一问题。假设您解决了第一个问题并按值返回您的std::reference_wrapper,那么您仍然在滥用std::reference_wrapper::operator=。该运算符不会为引用的对象分配新值。相反,它重新绑定引用以引用不同的实例。正确的用法是使用get()std::reference_wrapper 中获取正确的可分配int&amp;

#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&lt;int&gt;::reference_wrapper&lt;int&gt;(int&amp;&amp;),由编译器引入的转换调用。

【讨论】:

  • 很好的解释!
【解决方案2】:

std::ref 不仅返回一个引用,它还返回一个Reference Wrapper。具体来说,std::reference_wrapper&lt;T&gt;。因此,当您编写return std::ref(m_i); 时,您不会返回int&amp;,而是返回std::reference_wrapper&lt;int&gt; &amp;,这是对本身包含引用的悬空对象的引用。

按原样返回成员;编译器会理解它是一个参考。

#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;
}

考虑改为明确返回类型,因为在这种情况下,您不会从使用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;
}

【讨论】:

    猜你喜欢
    • 2015-10-25
    • 1970-01-01
    • 1970-01-01
    • 2016-07-27
    • 1970-01-01
    • 1970-01-01
    • 2021-08-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多