Would someone explain why the reference became invalid after going through an "identity" function, foo1? Isn't an "address" to A is passed into and returned by foo1?
struct A {
A(int x) : x_(x) {}
int x_;
};
int main() {
function<const A&(const A& r)> foo1 = [](const A& r) {
return r;
};
vector<A> vec{1, 2, 3};
cout << foo1(vec[0]).x_ << endl; // RUNTIME ERROR
return 0;
}
How does the problem line differ from:
const A& r = vec[0];
const A& r1 = r;
The problem is your lambda. It doesn't do what you think it does:
function<const A&(const A& r)> foo1 = [](const A& r) {
// ~~~~~~
return r;
};
Note that there's no trailing return type. That means that it's automatically deduced. Deduction never gives you a reference type, so this lambda returns an A, not an A const&. That returned temporary A is then bound to the return A const& of function's operator(). That temporary is not lifetime-extended. But the time we finish calling foo1(), we have a dangling reference to that temporary A. This is undefined behavior, which I guess with your compiler, gave you a helpful runtime error.
To fix this, you need to explicitly specify the return type:
function<const A&(const A& r)> foo1 = [](const A& r) -> A const& {
return r;
};
But even this is dangerous, since you can still pass a temporary A into this function and get a dangling reference out. No real way around that one.
The ease of falling into this trap is also LWG Issue 2813
While your function object returns const A& the lambda you provide it does not. It's return type is deduced from the return statement, which is deduced to be A. Try adding an explicit return type like this instead.
function<const A&(const A& r)> foo1 = [](const A& r) -> const A& {
return r;
};
Related
Let's say we have member class with two member functions defined as follows:
class SomeClass
{
private:
int val = {};
public:
const int getVarLRef() & {
return val;
}
const int getVarCLRef() const& {
return val;
}
};
int main()
{
auto var1 = SomeClass().getVarCLRef();
auto var2 = SomeClass().getVarLRef();
return 0;
}
I not quite understand what is the difference between const& and &.
Why it works with getVarCLRef if we specified this function as const&? Shouldn't it be allowed to be invoked only with lvalues?
getVarLRef, on the other hand, works just fine and fails to compile in this case as expected.
I use C++11 and gcc 7.3.0
Const and reference member function qualifiers are to be able to apply those qualifier to "this" as for regular parameter, so mainly, you have something like:
int getVarLRef(SomeClass& self) { return self.val; }
int getVarCLRef(const SomeClass& self) { return self.val; }
And there, I think you know that:
getVarCLRef(SomeClass()); // Valid, temporary can bind to const lvalue reference
getVarLRef(SomeClass()); // INVALID, temporary CANNOT bind to non-const lvalue reference
Shouldn't it be allowed to be invoked only with lvalues?
Because rvalue could be bound to lvalue-reference to const too. Just as the following code works.
const SomeClass& r = SomeClass();
On the other hand, rvalue can't be bound to lvalue-reference to non-const, then the invocation of getVarLRef fails as you expected.
A class with a 'const int&' data member causes the following compilation error when overloading operator= (compiler is g++):
assignment of read-only location.
When the data member is changed to 'const int*', it is fine.
Why?
#include <iostream>
using namespace std;
class B {
private:
const int& ir;
//const int* ir;
public:
B(const int& i) : ir(i) {}
//B(const int& i) : ir(&i) {}
B& operator=(B& b) { ir = b.ir; return *this; }
};
g++ error message:
opertostack.cpp:9:31: error: assignment of read-only location ‘((B*)this)-> B::ir’
B& operator=(B& b) { ir = b.ir; return *this; }
^~
You cannot rebind references. Once they are created, they need to refer to the same object for their entire lifetime.
However, your code has much bigger issues. Let's just remove operator=() so it compiles. We then instantiate a B:
B b(42);
b.ir was bound to the temporary int passed to the constructor. That temporary doesn't exists anymore after the constructor returns. b.ir is now a dangling reference. It refers to an object that does not exist anymore.
A pointer isn't going to help you either. If we change B::ir to be a const int* and switch the commented out code, then the result of instantiating a B as above is now a dangling pointer. It points to a temporary that doesn't exist anymore.
So in both cases, you get undefined behavior when using B::ir.
What you want here is just a normal int member. The constructor parameter doesn't need to be a reference either in this case. An int is just as easy to copy as a reference so you don't gain anything by using a reference parameter. Finally, the assignment operator should take a const reference, so that you can assign from const B objects as well:
class B {
private:
int ir;
public:
B(const int& i) : ir(i) {}
B& operator=(const B& b) { ir = b.ir; return *this; }
};
I have tried hard to make a lambda function return a value by reference without making a copy of the referenced value.
My code example below illustrates the problem. It compiles and runs ok, but with the "//" commented line instead of the line above, it doesn't.
I have found two workarounds (both illustrated in my example):
wrap the result with std::ref()
return a pointer instead of a reference
But both workarounds are not what I really want, and I do not understand why they are necessary: The expression "makeRefA()" has already the type the lambda function returns (const A&) and thus must neither be copied nor converted. By the way: The copy constructor is really called when I do not explicitly delete it (which in my "real" code is a performance problem).
To me it looks like a compiler bug, but I have tried with several C++11-compilers which all show up the same error. So is there something special concerning the "return" statement in a lambda function?
#include <functional>
#include <iostream>
struct A {
A(int i) : m(i) { }
A(const A&) = delete;
int m;
};
void foo(const A & a) {
std::cout << a.m <<'\n';
}
const A & makeRefA() {
static A a(3);
return a;
}
int main() {
std::function<const A&()> fctRef = [&]
{ return std::ref(makeRefA()); }; //compiles ok
//{ return makeRefA(); }; //error: use of deleted function 'A::A(const A&)'
foo(fctRef());
std::function<const A*()> fctPtr =
[&] { return &makeRefA(); };
foo(*fctPtr());
return 0;
}
output:
3
3
By default, the automatically-deduced type of a lambda is the non-reference version of a type
... the return type is the type of the returned expression (after lvalue-to-rvalue, array-to-pointer, or function-to-pointer implicit conversion); (source)
If you want a return type with a reference, you will have to specify it more explictly. Here are some options:
[&]()
-> decltype( makeRefA() )
{ return makeRefA()); };
or simply be fully explicit about the return type with ->
[&]()
-> const A&
{ return makeRefA(); }
If using C++14, then simply use decltype(auto),
[&]()
-> decltype(auto)
{ return makeRefA(); }
The rules for decltype can be complicated at times. But the fact that makeRefA() is an expression (as opposed to simply naming a variable) means that the type of the expression (const A&) is faithfully returned by decltype( makeRefA() ).
You can specify the return type
#include <functional>
#include <iostream>
struct A {
A(int i) : m(i) { }
A(const A&) = delete;
int m;
};
void foo(const A & a) {
std::cout << a.m <<'\n';
}
const A & makeRefA() {
static A a(3);
return a;
}
int main() {
std::function<const A&()> fctRef = [&]()->const A&
// { return std::ref(makeRefA()); }; //compiles ok
{ return makeRefA(); }; // works
foo(fctRef());
std::function<const A*()> fctPtr =
[&] { return &makeRefA(); };
foo(*fctPtr());
return 0;
}
According to http://en.cppreference.com/w/cpp/language/lambda, these rules apply to lambdas with no trailing return type:
In C++11, lvalue-to-rvalue, array-to-pointer, or function-to-pointer implicit conversion are applied to the type of the returned expression. (Here, the lvalue-to-rvalue conversion is what's hitting you.)
In C++14 and later, the type is deduced as for a function whose return type is declared auto; and that in turn follows the rules for template argument deduction. Then, since auto includes no reference specification, that means that references and cv-qualifiers will be ignored.
The effect is probably desirable in most situations: for example, in this lambda expression
[](const std::vector<int>& v) { return v[0]; }
you probably intend to return an int, even though std::vector<int>::operator[] const returns const int&.
As others have mentioned, you can override this behavior by giving an explicit trailing return type.
I made a wrapper around an object in my code that should modify accesses to the object. I choose to use an object here for testing instead of a functor that would have the same functionality. Basically: The wrapper receives a reference to the object and forwards all indexed accesses to the object (after some possible manipulation)
Now comes the problem: The accessor discards constness of the wrapped object.
Minimal Example
struct Foo
{
std::array<int, 2> data;
const int& operator()(int idx) const{
return data[idx];
}
int& operator()(int idx){
return data[idx];
}
};
struct Bar
{
Foo& ref;
Bar(Foo& r):ref(r){}
int& operator()(int idx) const{
return ref(idx);
}
};
template< typename T >
void test(const T& data){
data(1) = 4;
std::cout << data(1);
}
void main(){
Foo f;
test(f);
// Above call does not compile (as expected)
// (assignment of read-only location)
Bar b(f);
test(b); // This does compile and works (data is modified)
}
Declaring the ()-operator of Bar (the wrapper) "const", I'd expect to be all member accesses "const" to. So it shouldn't be possible to return an "int&" but only a "const int&"
However gcc4.7 happily compiles the code and the const is ignored. Is this the correct behavior? Where is this specified?
Edit:
On a related issue: If use typedefs in Foo like:
struct Foo
{
using Ref = int&;
using ConstRef = const int&; //1
using ConstRef = const Ref; //2
int* data; // Use int* to have same issue as with refs
ConstRef operator()(int idx) const{
return data[idx]; // This is possible due to the same "bug" as with the ref in Bar
}
Ref operator()(int idx){
return data[idx];
}
};
I noticed that //1 does work as expected but //2 does not. Return value is still modifiable. Shouldn't they be the same?
Yes, this is correct behaviour. The type of ref is Foo &. Adding const to a reference type1 does nothing—a reference is already immutable, anyway. It's like having a member int *p. In a const member function, its type is treated as int * const p, not as int const * p.
What you need to do is add const manually inside the const overload if you want it there:
struct Bar
{
Foo& ref;
Bar(Foo& r):ref(r){}
int& operator()(int idx) const{
return const_cast<const Foo&>(ref)(idx);
}
};
To address the question edit: no, the typedefs are not the same. const int & is a reference to a (constant int). const Ref is a constant Ref, that is, a constant (reference to int); parentheses used in mathematical sense.
1 I am talking about the reference type itself. Not to be confused with adding const to the type to which the reference refers.
Yeah, it is expected behaviour. The reason is that const for your method says only that reference wont be change not the referenced object. Reference is always unchanged so it is always true. Take a look at this code with pointer:
int i;
struct Bar
{
int* pi;
Foo& ref;
Bar(Foo& r):ref(r){}
int& operator()(int idx) const{
*pi = 4; // we can change pointed object
pi = &i; // Compile error: we can't change the pointer.
return ref(idx);
}
};
I have a class that 'remembers' a reference to some object (e.g. an integer variable). I can't have it reference a value that's destructed immediately, and I'm looking for a way to protect the users of my class from doing so by accident.
Is an rvalue-reference overload a good way to prevent a temporary to be passed in?
struct HasRef {
int& a;
HasRef(int& a):a(a){}
void foo(){ a=1; }
};
int main(){
int x=5;
HasRef r1(x);
r1.foo(); // works like intended.
HasRef r2(x+4);
r2.foo(); // dereferences the temporary created by x+4
}
Would a private rvalue overload do?
struct HasRef {
int& a;
HasRef( int& a ):a(a){}
void foo(){ a=1; }
private:
HasRef( int&& a );
};
... HasRef r2(x+1); // doesn't compile => problem solved?
Are there any pitfalls I didn't see?
If you have to store a const reference to some instance of type B into your class A, then surely you want to be ensured, that lifetime of A instance will be exceeded by the lifetime of B instance:
B b{};
A a1{b}; // allowed
A a2{B{}}; // should be denied
B const f() { return B{}; } // const result type may make sense for user-defined types
A a3{f()}; // should also be denied!
To make it possible you should explicitly to = delete; all the constructor overloadings, which can accept rvalues (both const && and &&). For this to achieve you should just to = delete; only const && version of constructor.
struct B {};
struct A
{
B const & b;
A(B const & bb) : b(bb) { ; } // accepts only `B const &` and `B &`
A(B const &&) = delete; // prohibits both `B &&` and `B const &&`
};
This approach allows you to prohibit passing to the constructor all kinds of rvalues.
This also works for built-in scalars. For example, double const f() { return 0.01; }, though it cause a warning like:
warning: 'const' type qualifier on return type has no effect [-Wignored-qualifiers]
it still can has effect if you just = delete; only && version of constructor:
struct A
{
double const & eps;
A(double const & e) : eps(e) {} // binds to `double const &`, `double &` AND ! `double const &&`
A(double &&) = delete; // prohibit to binding only to `double &&`, but not to `double const &&`
};
double const get_eps() { return 0.01; }
A a{0.01}; // hard error
A a{get_eps()}; // no hard error, but it is wrong!
For non-conversion constructors (i.e. non-unary) there is an issue: you may have to provide = delete;-d versions for all the combinatorically possible versions of constructors as follows:
struct A
{
A(B const &, C const &) {}
A(B const &&, C const &&) = delete;
// and also!
A(B const &, C const &&) = delete;
A(B const &&, C const &) = delete;
};
to prohibit mixed-cases like:
B b{};
A a{b, C{}};
Ignoring the fact the code isn't valid and just answering the question about the private overload...
In C++11 I would prefer a deleted function to a private function. It's a bit more explicit that you really can't call it (not even if you're a member or friend of the class.)
N.B. if the deleted constructor is HasRef(int&&)=delete it will not be chosen here:
int i;
HasRef hr(std::forward<const int>(i));
With an argument of type const int&& the HasRef(const int&) constructor would be used, not the HasRef(int&&) one. In this case it would be OK, because i really is an lvalue, but in general that might not be the case, so this might be one of the very rare times when a const rvalue reference is useful:
HasRef(const int&&) = delete;
That shouldn't compile. A good C++ compiler (or really almost any C++ compiler that I've ever seen) will stop that from happening.
I'm guessing you're compiling in MSVS. In that case, turn off language extensions and you should get an error.
Otherwise, not that even marking the reference const extends the lifetime of the temporary until the constructor finishes. After that, you'll refer to an invalid object.