const reference for temporary lifetime lengthening
I have a question about some C++ standard compliance or lack of it.
In my project I'm using some simple Guard class that uses the const reference trick. I'm using Visual Studio 2005 and there are two configurations - one for normal release build and the second one for unit tests.
In both cases there is some temporary hanging on the const reference in the end, but what happens in the meantime is the problem. For release configuration, the const reference points directly to the temp created in the return of the helper function template that creates the Guard instance ( no copy constructor is called, not even instantiated for that matter ).
But for unit test conf the function template temp is first copied and then its destructor is called, doing what should be done only after the const reference goes out of scope.
I have solved the开发者_开发技巧 problem by disabling the original guard in base class copy constructor ( so the action in destructor is not triggered for config for which copy constructor is called ), but what bothers me is:
Is the copy-the-temporary behaviour standard-compliant? Does the standard tells that the const reference should point directly to the temp, or is this implementation-defined behaviour not specified in the standard?
I have based my code roughly on Scope Guard article in DDJ and Herb Sutter's gotw 88 article, but both those sources don't seem to take the earlier destructor call into account.
Any info from someone more knowledgeable will be appreciated.
EDIT:
Ok the code is something like this:
class GuardBase
{
public:
GuardBase() : m_enabled(true)
{}
//this is done because in normal build no copy constructor is called ( directly using the function temporary)
//but for UT conf somehow the original temp is copied and destroyed
GuardBase(const GuardBase& other)
{
other.disable();
}
void disable() const
{
m_enabled = false;
}
protected:
//member is mutable because we will access the object through the const reference
mutable bool m_enabled;
};
template< typename Arg, typename ObjType, typename MemberMethod >
class Guard1Arg : public GuardBase
{
public:
Guard1Arg(ObjType& obj, MemberMethod remover, Arg arg) : m_arg(arg), m_remover(remover), m_object(obj)
{}
~Guard1Arg()
{
if ( m_enabled )
{
(m_object.*m_remover)(m_arg);
}
}
private:
Arg m_arg;
MemberMethod m_remover;
ObjType& m_object;
//this class should not be assigned
Guard1Arg& operator=(const Guard1Arg& other);
};
//utility template function used to create Guards using member functions with 1 argument
template<typename MemberFunction, typename Obj, typename Arg>
Guard1Arg<Arg, Obj, MemberFunction> MakeGuard1Arg(Obj& obj, MemberFunction memberFunction, Arg& arg)
{
return Guard1Arg<Arg, Obj, MemberFunction>(obj, memberFunction, arg);
}
#define GUARD_CREATE(arg, remover) const GuardBase& guard = MakeGuard1Arg(*this, remover, arg);
#define GUARD_DISABLE guard.disable();
#define GUARD_FRIEND template< typename Arg, typename ObjType, typename MemberMethod > friend class Guard1Arg;
Both behaviours are standards conforming. If you have code like this:
T foo()
{
return T();
}
int main()
{
const T& x = foo();
}
Then, conceptually, in foo
, a temporary object is created. This temporary is copied to the return-value of foo
. In main
, this copy (which is also a temporary object) is bound to x
.
The copy that is the return-value from foo
gets its lifetime extended, but not the temporary that was the source for the copy.
But, the C++ standard explicitly allows redundant temporary objects to be elided. So instead of creating a temporary and copying that over into the slot for return values, foo
can directly create the temporary in that slot.
Both options are possible, and the compiler does not even have to document when it uses which option.
The relevant sections of the C++ standard are 6.6.3 ([stmt.return]) and 12.2 ([class.temporary]).
精彩评论