Search code examples
c++referencec++14auto

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


I've got a simple snippet below for 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;
}

It compiles with error as below:

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;
    ^~~~~~~~~~~~~~~~~

I know, by removing the "ref" wrapper, the program gets compiled. I just wish to know what's the problem with using "ref" here?

  1. As long as I wish to return a reference, why adding a "ref" here causes error?
  2. I don't get the point why g++ saw it as "use of deleted function", which function is being deleted?

Solution

  • There are a number of problems with the code you've shared.

    The first problem is that you are returning a reference to an object that will no longer exist when your function returns. std::ref returns an std::reference_wrapper which will go out of scope. An std::reference_wrapper is an object that gives some of the functionalities of a reference type while still behaving like an object. It has move semantics, so the correct usage would be to return the std::reference_wrapper by value.

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

    Though as others have pointed it out, std::ref is not really needed here. You can just return a reference to m_i directly. If a user happens to actually need an std::reference_wrapper for that int, they can call std::ref themselves with the reference to m_i you provide.

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

    But that's not the only problem with your code. Assuming you fix the first issue and return your std::reference_wrapper by value, you are still misusing std::reference_wrapper::operator=. That operator does not assign a new value to the referred object. Instead, it rebinds the reference to refer to a different instance. The correct usage would be to use get() to get a proper assignable int& from the 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;
    }
    

    But what about the error message? The compiler sees that the only operator= that exists for std::reference_wrapper is one that takes another std::reference_wrapper. So it tries to implicitly convert 4 to an std::reference_wrapper the only way it can, by trying to use the constructor that takes a single argument. However, it's not possible to create an std::reference_wrapper to a literal like 4 (or rvalues in general). It needs a proper lvalue to refer to. This is prevented by declaring a constructor that accepts rvalue references to the wrapped type (in this case int) and deleting it. Attempting to use that constructor will produce the error you are seeing. The deleted function is std::reference_wrapper<int>::reference_wrapper<int>(int&&), which is called by the conversion introduced by the compiler.