我对某些C ++标准合规性或缺乏合规性有疑问。
在我的项目中,我使用了一些使用const引用技巧的简单Guard类。我正在使用Visual Studio 2005,有两种配置 - 一种用于正常发布版本,另一种用于单元测试。
在这两种情况下,最后都会在const引用上暂时挂起,但同时发生的问题是问题。对于发布配置,const引用直接指向在创建Guard实例的辅助函数模板的返回中创建的临时(不调用任何复制构造函数,甚至没有实例化)。
但是对于单元测试conf,首先复制函数模板temp,然后调用它的析构函数,只有在const引用超出范围之后才应该做。
我通过在基类复制构造函数中禁用原始防护来解决了这个问题(因此,对于调用哪个复制构造函数的配置,不会触发析构函数中的操作),但困扰我的是:
临时复制行为是否符合标准?标准是否告诉const引用应直接指向temp,或者是否在标准中未指定此实现定义的行为?
我的代码大致基于DDJ中的Scope Guard文章和Herb Sutter的88篇文章,但这两个来源似乎都没有考虑早期的析构函数。
来自知识渊博的人的任何信息都将受到赞赏。
编辑:
好的代码是这样的:
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;
答案 0 :(得分:4)
这两种行为都符合标准。 如果你有这样的代码:
T foo()
{
return T();
}
int main()
{
const T& x = foo();
}
然后,从概念上讲,在foo
中创建一个临时对象。此临时值将复制到foo
的返回值。在main
中,此副本(也是临时对象)绑定到x
作为foo
的返回值的副本会延长其生命周期,但不是作为副本源的临时值。
但是,C ++标准明确允许冗余的临时对象被省略。因此,foo
可以直接在该插槽中创建临时值,而不是创建临时值并将其复制到插槽中以获取返回值。
这两个选项都是可能的,编译器甚至不必记录何时使用哪个选项。
C ++标准的相关部分是6.6.3([stmt.return])和12.2([class.temporary])。