In both of these snippets of code a temporary is created for the reference to refer to. A temporary cannot bind to a non-const reference, so the second snippet is illegal.
Of course, using the reference in the first snippet after the line where it was created will result in undefined behavior. The temporary it referenced will have been destroyed.
Of course, using the reference in the first snippet after the line where it was created will result in undefined behavior. The temporary it referenced will have been destroyed.
The question raised my attention since I was learning creating a class constructor, one of whose arguments is type const string &, and I saw that parameters passed to it are all C style strings.
Due to what you said, is it unsafe to set an argument type as const string & to receive C style strings, cause it has a undefined behavior, may destroyed. Should I useconst string get better safety?
Another question,
A temporary cannot bind to a non-const reference, so the second snippet is illegal.
Is this a C++ rule? Is it only for class cases, or to all cases?
Due to what you said, is it unsafe to set an argument type as const string & to receive C style strings, cause it has a undefined behavior, may destroyed. Should I useconst string get better safety?
In Someclass c("literal"); the temporary created will not be destroyed until the full expression is evaluated, which means it will not be destroyed until after the constructor returns. No undefined behavior there.
Is this a C++ rule? Is it only for class cases, or to all cases?
> using the reference in the first snippet after the line where it was created will result in undefined behavior.
> The temporary it referenced will have been destroyed.
A reference to an anonymous temporary object extends the lifetime of the anonymous temporary to that of the reference itself (except in a few specific situations).
First I thought the reason why the reference must be const is that the temporary object is a const. The thinking comes from that temporary basic types cannot be modified.
Later, I find that temporary objects don't have to be const that aren't const by default. But why the reference referred to it must be const?
An rvalue reference (to non-const) can extend the lifetime of an anonymous temporary as a modifiable object.
Just as an lvalue reference to const can extend lifetime of an anonymous temporary as a non-modifiable object.
> When comes to function return value, it doesn't work.
It doesn't. It is one of the four "(except in a few specific situations)" that I had mentioned earlier (#3 below):
There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression.
...<elided>...
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound ...<elided>... persists for the lifetime of the reference except:
— A temporary bound to a reference member in a constructor’s ctor-initializer persists until the constructor exits.
— A temporary bound to a reference parameter in a function call persists until the completion of the full-expression containing the call.
— The lifetime of a temporary bound to the returned value in a function return statement is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
— A temporary bound to a reference in a new-initializer persists until the completion of the full-expression containing the new-initializer.
#include <iostream>
#include <cstring>
struct A
{
A( constchar* cstr )
{
sz = ( cstr ? std::strlen(cstr) : 0 ) + 1 ;
buff = newchar[sz]{} ;
if(cstr) std::strcpy( buff, cstr ) ;
std::cout << "A{ \"" << buff << "\" } constructed " ;
dump() ;
}
~A()
{
std::cout << "A{ \"" << buff << "\" } destroyed\n" << std::flush ;
std::memset( buff, 0, sz ) ;
delete[] buff ;
}
void dump() const
{
std::cout << "{ " ;
for( std::size_t i = 0 ; i < sz ; ++i ) std::cout << int( buff[i] ) << ' ' ;
std::cout << "}\n" ;
}
A( const A& ) = delete ;
A& operator= ( const A& ) = delete ;
char* buff ;
std::size_t sz ;
};
struct S
{
S( const A& r ) : a(r) {}
const A& a ;
};
const A& foo( const A& arg ) { return arg ; }
const A& bar() { return A("three") ; }
int main()
{
// A temporary bound to a reference member in a constructor’s ctor-initializer
// persists until the constructor exits.
{
S one( A("one") ) ;
// undefined behaviour (access object after it has been destroyed)
one.a.dump() ; // UB
}
// A temporary bound to a reference parameter in a function call
// persists until the completion of the full-expression containing the call.
{
const A& two = foo( A("two") ) ;
// undefined behaviour (access object after it has been destroyed)
two.dump() ; // UB
}
// The lifetime of a temporary bound to the returned value in a function
// return statement is not extended; the temporary is destroyed
// at the end of the full-expression in the return statement.
{
const A& three = bar() ;
// undefined behaviour (access object after it has been destroyed)
three.dump() ; // UB
}
// A temporary bound to a reference in a new-initializer persists until the
// completion of the full-expression containing the new-initializer.
{
struct B { const A& a ; };
B* pb = new B{ A("four") } ;
// undefined behaviour (access object after it has been destroyed)
pb->a.dump() ; // UB
}
}