临时寿命延长的const参考

时间:2010-11-03 11:19:54

标签: c++ standards scopeguard

我对某些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;

1 个答案:

答案 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])。