Re: Reference and temporary as aruments of conditional operator lead to dangerous behavior.
Am 23.07.2011 02:44, schrieb Alexei Sergeev:
Hello! I've got an error and came to this basic sample while investigating.
#include<iostream>
class A
{
public:
A() {}
A( const A& ) { std::cout<< "A( const A& )\n"; }
virtual ~A() { }
};
class B : public A
{
public:
B() {}
B( const B& other): A(other) { std::cout<< "B( const B& )\n"; }
virtual ~B() { }
};
int main()
{
B b;
A *aPtr =&b;
const A&aRef = aPtr ? *aPtr : A();
const B&bRef = dynamic_cast< const B&>( aRef );
return 0;
}
$g++ -W -Wall -std=c++0x test.cpp
test.cpp: In function ?int main()?:
test.cpp:25:14: warning: unused variable ?bRef?
$ ./a.out
A( const A& )
terminate called after throwing an instance of 'std::bad_cast'
what(): std::bad_cast
gcc version 4.5.2 (Gentoo 4.5.2 p1.0, pie-0.4.5)
This is dangerous behavior, because conditional operator seem obvious in this context:
void function( const A& );
...
B someB;
A *a =&someB;
function( a ? *a : A() );
in this context B always will be reduced to A when passed "by reference".
Is this behavior by standard?
Yes, the behaviour is now clearly specified after resolving the core language issues
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#446
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#86
to always produce a copy of either operand in this scenario. I consider the committee decision as clean and safe in this situation, and I would rather argue that your original query code which mixes glvalues (the second operand) and prvalues (the third operand) is very dangerous to write in the first place.
It is possible to fix this using static_cast:
const A&aRef = aPtr ? *aPtr : static_cast< const A& >( A() );
but the code became hardly readable.
This works, because you ensure that the third operand is a glvalue as well.
It may look harder to read but it also has the advantage for emphasizing the differences of the value categories of the expressions *aPtr and A().
Will this template solve the problem correctly and how portable is it?
template<class T> const T&ToRefOrTemp( const T *ptr )
{
if ( ptr )
{
return ptr;
}
return T();
}
const A&aRef = ToRefOrTmp( aPtr );
This is even worse, because the life-time of the temporary created within the function won't be lengthened (see bullet 3 of the bulleted list in 12.2p5), so it would be destroyed just after the initialization of the reference aRef.
HTH & Greetings from Bremen,
Daniel Kr?gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]