After researching a bit I don't understand the output (source code below):
42
42
45
I'm fine with the second, but why I get this output?
It's coming from fooling around with avoiding global constants and variables in a bigger project. Could someone please explain it to me?
#include <iostream>
class Const
{
public:
Const() = delete;
static auto foo(int val = 42) -> int&;
};
auto Const::foo(int val) -> int&
{
static int sval = val;
return sval;
}
int main()
{
std::cout << Const::foo() << std::endl;
Const::foo(24);
std::cout << Const::foo() << std::endl;
Const::foo() = 45;
std::cout << Const::foo() << std::endl;
return 0;
}
In your code:
#include <iostream>
class Const // what a strange name for something that is not const.
{
public:
Const() = delete;
static auto foo(int val = 42) -> int&;
};
auto Const::foo(int val) -> int& // if you don't return a const reference,
// it may be modified by the caller
{
static int sval = val; // val is not const, and only initialized
// once.
return sval; // you return a reference to a mutable value.
}
int main()
{
std::cout << Const::foo() << std::endl;
Const::foo(24); // this changes nothing, the static variable sval
// has already been initialized.
std::cout << Const::foo() << std::endl;
Const::foo() = 45; // the reference returned by foo() is
// not const. SO that works.
std::cout << Const::foo() << std::endl;
return 0;
}
To fix, Const::foo() should return a const int&.
Forget about using static function variables. When entering the function, the code must check each time if its static variables have been initialized. This usually involves using a hardware fence or some other thread-safe mechanism. These will unnecessarily slow down execution, especially when accessing these const values from multiple threads.
Related
I have read many articles regarding the lifetime of a temporary and it seems the lifetime of temporary object is extended in certain cases, while in the rest of the cases, its a dangling object.
In my case the temporary object is returned from a function, I want to understand if the const ref to the temporary object will remain valid or not.
Here is the code:
class MyClass
{
public:
std::vector<int> vec{ 1, 2 };
MyClass()
{
cout << "Ctor" << endl;
}
MyClass(const MyClass ©)
{
vec = copy.vec;
cout << "Copy Ctor" << endl;
}
~MyClass()
{
cout << "Dtor" << endl;
}
};
MyClass access()
{
MyClass obj;
obj.vec[0] = 10;
return obj;
}
int main()
{
{
const auto &ret = access(); // calls the copy-ctor and assigns the temporary to reference 'ret'
auto val = ret.vec[0];
cout << "By Ref = " << val << endl; // works fine
}
cout << "_____________________________________" << endl;
{
const auto *ret = &access(); // temporary is lost
auto val = ret->vec[0]; // program crash
cout << "By Pointer = " << val << endl;
}
return 0;
}
Only temporaries bound to const references have their lifetime extended, with the exception of const references bound to return of functions, such as
const int& f(){int x{42}; return x;}
int main()
{
const int& bad = f(); // you'll end up with a dangling reference
}
or
const int& f(const int& x)
{
return x;
}
int main(){
{
const int& bad = f(42); // you'll end up (again) with a dangling reference
}
In your second case you have a pointer, so the lifetime of the right hand side temporary is not prolonged.
This question already has answers here:
const reference to a temporary object becomes broken after function scope (life time)
(2 answers)
Closed 4 years ago.
For regular local const reference variables, the scope is prolonged. Which is why the following code works as expected:
#include <iostream>
#include <memory>
struct foo
{
foo()
{
std::cout << "foo() #" << (void*)this << std::endl;
}
~foo()
{
std::cout << "~foo() #" << (void*)this << std::endl;
}
};
int main()
{
auto const& f = std::make_shared<foo>();
std::cout << "f = " << f.get() << std::endl;
return 0;
}
// prints:
// foo() #0x55f249c58e80
// f = 0x55f249c58e80
// ~foo() #0x55f249c58e80
It seems though that this does not work as expected when assigning a moved object using std::move():
#include <iostream>
#include <memory>
#include <list>
struct foo
{
foo()
{
std::cout << "foo() #" << (void*)this << std::endl;
}
~foo()
{
std::cout << "~foo() #" << (void*)this << std::endl;
}
};
int main()
{
std::list<std::shared_ptr<foo>> l;
l.push_back(std::make_shared<foo>());
auto const& f = std::move(l.front());
l.clear();
std::cout << "f = " << f.get() << std::endl;
return 0;
}
// prints
// foo() #0x564edb58fe80
// ~foo() #0x564edb58fe80
// f = 0x564edb58fe80
Does std::move() indeed change the scope, or am I dealing with a compiler bug?
Changing the variable from auto const& f to just auto f fixes the problem. If I wrap the move into another function, it also works:
auto const& f = [&]() { return std::move(l.front()); }();
It's almost like std::move() does not share the same semantics as a function call, but rather as if it was just a regular variable assignment:
auto const& f = std::move(l.front());
Let's put aside std::move() and create this function:
struct sometype {};
const sometype &foobar( const sometype &cref ) { return cref; }
and now we use it:
const sometype &ref = foobar( sometype() );
What do you think, will lifetime of temporary be prolongated in this case? No it would not. Lifetime is only prolongated when you assign to a reference directly. When it goes through a function or through static_cast or std::move that prolongation is gone. So you have exactly the same issue with std::move()
struct sometype {
sometype() { std::cout << "ctype()" << std::endl; }
~sometype() { std::cout << "~ctype()" << std::endl; }
};
const sometype &foobar( const sometype &cref ) { return cref; }
int main()
{
const sometype &cref1 = foobar( sometype() );
sometype &&cref2 = std::move( sometype() );
std::cout << "main continues" << std::endl;
}
output:
ctype()
~ctype()
ctype()
~ctype()
main continues
live example
Note: you should not use std::move() on return statement, it does not give you anything.
For your code change. You should remember that std::move() does not move anything. It is done by special assignment operator or constructor (if they provided for a type). So when you write this code:
const type &ref = std::move( something );
there is no constructor nor assignment operator involved and so no moving happens. For actual moving to happen you have to assign it to a variable:
type val = std::move( something );
now moving would happen if possible or copy otherwise.
I'm wondering if it's by chance a pointer to const reference bound to a destroyed stack variable can be working.
I read const reference lifetime is extended on rvalues, so this is "normal" const reference works but at the end of the ctor of Storage ref should be destroyed, isn't it ?
Does const reference lifetime is also extended because I retrieved it's address in a pointer or is this pure luck ?
Live example
#include <iostream>
class Storage
{
public:
Storage(const int& ref)
{
p = &ref;
}
const int* Get() const
{
return p;
}
private:
const int* p;
};
int main()
{
Storage* s = nullptr;
{
int someValue = 42;
std::cout << &someValue << std::endl;
s = new Storage(someValue);
}
const int* p = s->Get();
std::cout << p << std::endl;
std::cout << *p << std::endl;
}
It's also working with a struct Live example
#include <iostream>
struct Dummy
{
int value;
};
class Storage
{
public:
Storage(const Dummy& ref)
{
p = &ref; // Get address of const reference
}
const Dummy* Get() const
{
return p;
}
private:
const Dummy* p;
};
int main()
{
Storage* s = nullptr;
{
Dummy dummy;
dummy.value = 42;
std::cout << &dummy << std::endl;
s = new Storage(dummy);
}
const Dummy* p = s->Get();
std::cout << p << std::endl;
std::cout << p->value << std::endl;
}
Your s variable indeed has a dangling pointer as someValue has fallen out of scope. Your code therefore exhibits undefined behavior.
Your comment about "const reference lifetime is extended on rvalues" is true in some circumstances, but someValue is an lvalue.
The compiler probably assigns the stack space for all the local variables at the start, so someValue is still in the same place in memory even after it goes out of scope. Of course a different compiler might do something different, and it could get overwritten by a subsequent local variable.
For your second example if you wrote a ~Dummy(){ value=0; }, then it wouldn't work, proving that the lifetime of the dummy object hasn't been extended.
The following is allowd in C++ through promotion:
int ivalue = true;
bool bvalue = 1;
Thats okay. And this is not allowed, through type-checking:
int& ivalue = false;
bool& bvalue = 0;
Thats okay.
Look on this, from Wikipedia.
http://en.wikipedia.org/wiki/Property_(programming)#C.2B.2B
#include <iostream>
template <typename T> class property {
T value;
public:
T & operator = (const T &i) {
::std::cout << "T1: " << i << ::std::endl;
return value = i;
}
// This template class member function template serves the purpose to make
// typing more strict. Assignment to this is only possible with exact identical
// types.
template <typename T2> T2 operator = (const T2 &i) {
::std::cout << "T2: " << i << ::std::endl;
T2 &guard = value;
return value = i;
throw guard; // Never reached.
}/**/
operator T const & () const {
return value;
}
};
struct Bar {
// Using the property<>-template.
property <bool> alpha;
property <unsigned int> bravo;
};
int main () {
Bar bar;
bar.alpha = true;
bar.bravo = true; // This line will yield a compile time error
// due to the guard template member function.
::std::cout << foo.alpha << ", "
<< foo.bravo << ", "
<< bar.alpha << ", "
<< bar.bravo
<< ::std::endl;
bool bvar = 22;
int ivar = true;
//int &newvar = bvar;
print(bvar);
::std::cout << bvar << " and " << ivar << "\n";
return 0;
}
I think with using of templates the type-checking for references get lost? Am I right?
No, type conversions and reference binding rules are the same whether or not there's a template involved. You can bind a temporary (such as the one you need when the referee is a different type to the reference) to a const reference, but not to a non-const reference.
That is why your second pair of examples fails to compile, and also why the template operator= fails to compile in the larger example when the argument type doesn't match the template parameter. In both cases, the code tries to create a temporary through type-conversion and bind it to a non-const reference.
I need to pass to function pointer to int.
Now if I want to pass 5 I'm doing it like this:
int * i = NULL;
int b = 5;
i = &b;
Is there any better way to write it shorter?
I want to pass bytes that are in i int to this function:
void Write2Asm(void* pxAddress, BYTE * MyBytes, int size)
You can just pass &b to the function; no need for an intermediate pointer variable.
Why to create a pointer variable?. Why can't you do it like this?.
int b = 5;
func(&b)
void f(int *i)
{
//...
}
int b = 5;
f(&b);
is enough!
There are a few old C APIs that always take arguments by pointer, even if they're effectively read-only booleans etc.. I'm not recommending it - more for interest's sake - but if you want to go the whole hog you could do something hackish like:
#include <iostream>
struct X
{
X(int n) : n_(n) { std::cout << "X()\n"; }
~X() { std::cout << "~X()\n"; }
operator int&() { return n_; }
operator const int() const { return n_; }
int* operator&() { return &n_; }
const int* operator&() const { return &n_; }
int n_;
};
// for a function that modifies arguments like this you'd typically
// want to use the modified values afterwards, so wouldn't use
// temporaries in the caller, but just to prove this more difficult
// case is also possible and safe...
void f(int* p1, int* p2)
{
std::cout << "> f(&" << *p1 << ", &" << *p2 << ")\n";
*p1 += *p2;
*p2 += *p1;
std::cout << "< f() &" << *p1 << ", &" << *p2 << "\n";
}
int main()
{
// usage...
f(&X(5), &X(7));
std::cout << "post\n";
}
Crucially, the temporaries are valid until after the function call f(...) exits.