The following code yields a Segmentation Fault on the y = anotherFunctor() line. As far as I understand, this happens because the globalFunctor variable does not exist when anotherFunctor is created. But why does it work if I replace std::function<int(int)> with GlobalFunctor? How would I fix it?
#include <functional>
struct GlobalFunctor
{
int operator()() const { return 42; }
};
extern GlobalFunctor globalFunctor;
struct AnotherFunctor
{
AnotherFunctor() : g_(globalFunctor) {}
int operator()() const { return g_(); }
const std::function<int()>& g_;
} anotherFunctor;
GlobalFunctor globalFunctor;
int main()
{
AnotherFunctor af;
int x = af();
int y = anotherFunctor();
int z = x + y;
return 0;
}
Edit: I tried compiling this with clang instead of gcc and it warns me about binding reference member 'g_' to a temporary value -- but it crashes when compiling this. Would the cast to std::function create a temporary reference?
At g_(globalFunctor), globalFunctor has to be converted to an std::function because it is of type GlobalFunctor. So a temporary is produced and this is bound to the constant reference. You could think of the code as doing g_(std::function<int()>(globalFunctor)). However, this temporary only lives until the end of the constructor, as there is a special rule in C++ saying that temporaries in member initializer lists only live until the end of the constructor. This leaves you with a dangling reference.
The code works when you replace std::function<int(int)> with GlobalFunctor because no conversion is involved. Therefore, no temporaries are produced and the reference directly refers to the global object.
You either need to not use references and store a std::function internally or make a global std::function and have a reference to that.
Related
I have this small piece of code that doesn't compile, and I was wondering about the reasons more specifically why is push(new X) the problem?
Before that, I would like to ask what new X (with no identifier) really mean? does it create an object X with no identifier name? does it go through the constructor at all?
Secondly,
I might not understand the whole concept, but, push is a template class, right?
Since it contains a static T stack, the type T is consistent and can not be changed once a specific argument is sent, the first one, in this case int x. Is that correct?
Thirdly,
following my second question, if it is correct then why push(y) do not flag a compiler error just like push(new X) does? i mean, x is an int type and y is a reference type.
I'd really appreciates if someone can clarify it for me.
Thanks, here it is:
#include <iostream>
using namespace std;
template <class T>
void push(T &t) {
static const int CAPACITY = 20;
static T stack[CAPACITY];
static int size = 0;
stack[size++] = t;
cout << size;
};
class X {};
void main()
{
int x = 3;
int &y = x;
push(x);
push(new X);
push(y);
}
and I was wondering about the reasons more specifically why is push(new X) the problem?
Because you declared type of your template function parameter as non const lvalue reference to T. To help programmers to avoid incorrect code language does not allow to bind temporaries to non const lvalue references. In your case:
new X
returns a temporary of type X * (pointer to X), your template function argument type T deduced to X * in this case and t is now lvalue reference to X * or X *& but language does not allow to bind temporary to an lvalue reference hense compilation error. Your second case is logically equal to this code:
void pushX( X *&t ); // pushX accepts reference to pointer to X
pushX( new X ); // you cannot bind a temporary returned by `new X` to lvalue reference `t`
It could be easier to understand if you use simple type like int:
int function_returns_int();
void push_int( int &ri );
push_int( function_returns_int() ); // compilation error, function_returns_int() returns temporary
You can make your code compile if you store pointer in a variable and make it non-temporary:
int main() // main() must have return type int not void
{
int x = 3;
int &y = x;
push(x);
X *px = new X;
push(px);
push(y);
delete px;
}
but most probably you chosen wrong type for the argument.
Details about references and why you can pass int &y and why t would not be a reference to reference in this case you can find here
Note: your code if you make it compile (changing type of t to const reference for example) would lead to memory leak, but that is out of scope of your question
dcl.type.cv provides an interesting example:
For another example,
struct X {
mutable int i;
int j;
};
struct Y {
X x;
Y();
};
const Y y;
y.x.i++; // well-formed: mutable member can be modified
y.x.j++; // ill-formed: const-qualified member modified
Y* p = const_cast<Y*>(&y); // cast away const-ness of y
p->x.i = 99; // well-formed: mutable member can be modified
p->x.j = 99; // undefined: modifies a const member
which indicates that, via const_cast, one may modify mutable members of a const qualified object, while you can't do that with non-mutable members.
To my understanding, this is because of the original constness of y itself. What would happen if we got rid of the mutable keyword, the const qualifier fot y, but modified the fields in a const method?
Example below:
#include <vector>
struct foo {
std::vector<int> vec{};
void bar() const {
auto& raw_ref = const_cast<std::vector<int>&>(vec);
raw_ref.push_back(0); // ok?
auto* raw_this = const_cast<foo*>(this);
raw_this->vec.push_back(0); // ok?
}
};
int main() {
foo f{};
f.bar();
}
Does it exhibit Undefined Behaviour? I would think that it does not, since we're modifying an originally non-const, but in a const context.
Additionally, notice that I provided two ways of modifying the vec. One with non-const reference and one with non-const pointer to this (which was originally const in this constext due to foo::bar being a const method). Do they differ in any particular way, given the question's context? I would assume that both are okay here.
Disclaimer: I am aware of mutable keyword, but this desing (not only being flawed) is simply an example. One could assume that the author of the code wanted to prohibit every single way of modifying vec except for push_backs.
Your quoted paragraph actually spelled out exactly what is undefined [dcl.type.cv]
Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime results in undefined behavior.
A const reference/pointer to a non-const object doesn't make that object a const object, all your accesses are well-formed.
When an 'auto' var is initialized using a function that returns a reference, why the var type is not a reference?
e.g. In following example, why type of x is Foo and not Foo& ?
class TestClass {
public:
Foo& GetFoo() { return mFoo; }
private:
Foo mFoo;
};
int main()
{
TestClass testClass;
auto x = testClass.GetFoo(); // Why type of x is 'Foo' and not 'Foo&' ?
return 0;
}
EDIT: The link explains how to get the reference, but my question is the reason for this behavior.
Because it would be annoying if it worked that way. How, for example, would you specify that you didn't want a reference?
When you use auto, you need to put const, &, &&, and volatile in yourself.
auto& x = testClass.GetFoo();
is your fix.
C++11 auto type inference rules drop reference, const and volatile qualifiers.
However, you may ask C++ compiler to use decltype type inference rules to keep all these qualifiers for declaring variable type. In your case it may be:
decltype(auto) x = testClass.GetFoo();
But this code can cause some side effects like reference to destroyed object, so you need to keep in mind the real variable type and life time.
How is it possible that this example works? It prints 6:
#include <iostream>
#include <functional>
using namespace std;
void scopeIt(std::function<int()> &fun) {
int val = 6;
fun = [=](){return val;}; //<-- this
}
int main() {
std::function<int()> fun;
scopeIt(fun);
cout << fun();
return 0;
}
Where is the value 6 stored after scopeIt is done being called? If I replace the [=] with a [&], it prints 0 instead of 6.
It is stored within the closure, which - in your code - is then stored within std::function<int()> &fun.
A lambda generates what's equivalent to an instance of a compiler generated class.
This code:
[=](){return val;}
Generates what's effectively equivalent to this... this would be the "closure":
struct UNNAMED_TYPE
{
UNNAMED_TYPE(int val) : val(val) {}
const int val;
// Above, your [=] "equals/copy" syntax means "find what variables
// are needed by the lambda and copy them into this object"
int operator() () const { return val; }
// Above, here is the code you provided
} (val);
// ^^^ note that this DECLARED type is being INSTANTIATED (constructed) too!!
Lambdas in C++ are really just "anonymous" struct functors. So when you write this:
int val = 6;
fun = [=](){return val;};
What the compiler is translating that into is this:
int val = 6;
struct __anonymous_struct_line_8 {
int val;
__anonymous_struct_line_8(int v) : val(v) {}
int operator() () const {
return val; // returns this->val
}
};
fun = __anonymous_struct_line_8(val);
Then, std::function stores that functor via type erasure.
When you use [&] instead of [=], it changes the struct to:
struct __anonymous_struct_line_8 {
int& val; // Notice this is a reference now!
...
So now the object stores a reference to the function's val object, which becomes a dangling (invalid) reference after the function exits (and you get undefined behavior).
The so-called closure type (which is the class type of the lambda expression) has members for each captured entity. Those members are objects for capture by value, and references for capture by reference. They are initialized with the captured entities and live independently within the closure object (the particular object of closure type that this lambda designates).
The unnamed member that corresponds to the value capture of val is initialized with val and accessed from the inside of the closure types operator(), which is fine. The closure object may easily have been copied or moved multiple times until that happens, and that's fine too - closure types have implicitly defined move and copy constructors just as normal classes do.
However, when capturing by reference, the lvalue-to-rvalue conversion that is implicitly performed when calling fun in main induces undefined behavior as the object which the reference member referred to has already been destroyed - i.e. we are using a dangling reference.
The value of a lambda expression is an object of class type, and
For each entity
captured by copy, an unnamed non-static data member is declared in the closure type.
([expr.prim.lambda]/14 in C++11)
That is, the object created by the lambda
[=](){return val;}
actually contains a non-static member of int type, whose value is 6, and this object is copied into the std::function object.
So here is my little class:
class test{
public:
int& addressofx();
private:
int x;
};
int& test::addressofx(){
return x;
}
and here is main:
int main(){
test a;
int& foo = a.addressofx();
}
Why does the above compile fine, but when I make the function addressofx into a regular int function(not int&), I get errors?
You can do this:
int x;
int &y = x;
without getting the address of x, but when you return x from a function you need its address? Why?
You're not returning the address of anything. You're returning a reference.
This code will return an address:
class test {
public:
int* addressofx();
private:
int x;
};
int* test::addressofx() {
return &x;
}
int main() {
test a;
int* foo = a.addressofx();
}
int& addressofx();
This is returning a reference to an int variable.
int& foo = a.addressofx();
This is declaring a non-const lvalue reference (lvalue means it has a variable name assigned to it, ie it can appear on the left side of an expression) that holds a reference to an int variable. Since the function returns a reference to an int, the compiler is happy.
when I make the function addressofx into a regular int function(not int&), I get errors?
A non-const lvalue reference cannot be bound to a temporary value (what addressofx() would return if you make that change). That is what rvalue references (introduced in C++11) are designed to handle instead, but you are not using that feature in this example.
You can do this:
int x;
int &y = x;
Because you are declaring an lvalue reference and binding it to an lvalue variable. That is allowed.
If you change addressofx() to return an int (which is an rvalue, because it has no name so it can only appear on the right side of an expression), the return value cannot be bound to a non-const lvalue reference, because it is not a real variable. However, it can be bound to a const lvalue reference (and the compiler will then extend the lifetime of the temporary beyond the function's return), eg:
int addressofx();
...
const int& foo = a.addressofx();
In short, you need to read up on how references work, and the difference between lvalue and rvalue. Not all combinations can be used together.