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?
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.