3

C++標準への準拠またはその欠如について質問があります。

私のプロジェクトでは、const参照トリックを使用するいくつかの単純なGuardクラスを使用しています。Visual Studio 2005を使用していますが、2つの構成があります。1つは通常のリリースビルド用で、もう1つは単体テスト用です。

どちらの場合も、最終的にconst参照に一時的にぶら下がっていますが、その間に何が起こるかが問題です。リリース構成の場合、const参照は、Guardインスタンスを作成するヘルパー関数テンプレートの戻りで作成された一時を直接指します(コピーコンストラクターは呼び出されず、そのことについてはインスタンス化されません)。

ただし、単体テストconfの場合、関数テンプレートtempが最初にコピーされ、次にそのデストラクタが呼び出され、const参照がスコープ外になった後にのみ実行する必要があります。

基本クラスのコピーコンストラクターで元のガードを無効にすることで問題を解決しました(したがって、コピーコンストラクターが呼び出される構成に対してデストラクタのアクションはトリガーされません)が、気になるのは次のとおりです。

一時的なコピーの動作は標準に準拠していますか?標準は、const参照が直接tempを指す必要があることを示していますか、それともこの実装定義の動作は標準で指定されていませんか?

私はコードをDDJのScopeGuardの記事とHerbSutterのgotw88の記事に大まかに基づいていますが、これらのソースはどちらも以前のデストラクタ呼び出しを考慮していないようです。

より知識のある人からの情報をいただければ幸いです。

編集:

コードは次のようなものです。

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;
4

1 に答える 1

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])です。

于 2010-11-03T11:48:20.117 に答える