4

コピー不可能なオブジェクトの値の初期化に関して、g++ と msvc の間でいくつかの異なる動作が見られます。コピー不可能なクラスを考えてみましょう:

class noncopyable_base
{
public:
    noncopyable_base() {}

private:
    noncopyable_base(const noncopyable_base &);
    noncopyable_base &operator=(const noncopyable_base &);
};

class noncopyable : private noncopyable_base
{
public:
    noncopyable() : x_(0) {}
    noncopyable(int x) : x_(x) {}

private:
    int x_;
};

タイプが POD の場合でも値が既知の値になるように、値の初期化を使用するテンプレート:

template <class T>
void doit()
{
    T t = T();
    ...
}

そしてそれらを一緒に使用しようとしています:

doit<noncopyable>();

これは、VC++ 9.0 の時点で msvc で正常に動作しますが、コピー コンストラクターがプライベートであるため、これをテストした g++ のすべてのバージョン (バージョン 4.5.0 を含む) で失敗します。

2 つの質問:

  1. 標準に準拠しているのはどの動作ですか?
  2. gcc でこれを回避する方法の提案 (明確にするために、これを変更することT t;は、POD タイプを壊すため、受け入れられる解決策ではありません)。

PS boost::noncopyable でも同じ問題が発生します。

4

4 に答える 4

8

MSVCで見られる動作は拡張機能ですが、次のページ(強調私のもの)に回りくどい方法で文書化されていますhttp://msdn.microsoft.com/en-us/library/0yw5843c.aspx

生成されたコードはほとんどの場合同じですが、等号初期化構文は関数スタイルの構文とは異なります。違いは、等号構文を使用する場合、コンパイラは次の一連のイベントが発生したかのように動作する必要があることです。

  • 初期化されるオブジェクトと同じタイプの一時オブジェクトを作成します。
  • 一時オブジェクトをオブジェクトにコピーします。

コンパイラーがこれらのステップを実行する前に、コンストラクターにアクセスできる必要があります。ほとんどの場合、コンパイラーは一時的な作成とコピーのステップを排除できますが、アクセスできないコピーコンストラクターにより、等号の初期化が失敗します(/ Za、/ Ze(言語拡張の無効化)の下)。

boost::value_initializedベンの回答へのコメントでlitbが指摘しているように、の簡略化されたバージョンである回避策については、 BenVoigtの回答を参照してください。のドキュメントにboost::value_initalizedは、問題、回避策、およびさまざまなコンパイラの問題の落とし穴についてのすばらしい説明があります。

于 2010-04-19T23:45:37.617 に答える
5

テンプレートのメタプログラミングは必要ないと思います。試す

template <class T>
void doit()
{
    struct initer { T t; initer() : t() {} } inited;
    T& t = inited.t;
    ...
}
于 2010-04-20T01:22:02.933 に答える
3

§12.8/14 があります:

オブジェクトのコピー コンストラクターまたはコピー代入演算子が暗黙的に使用され、特別なメンバー関数にアクセスできない場合、プログラムは不適切な形式です。

そして、§12.8/15 があります。

特定の基準が満たされると、オブジェクトのコピー コンストラクターやデストラクタに副作用がある場合でも、実装はクラス オブジェクトのコピー構築を省略できます。

したがって、問題は実際には、実装がコピー コンストラクターの呼び出しを省略した場合 (これは明らかに許可されています)、コピー コンストラクターは実際に使用されるのでしょうか?

そして、§3.2/2 によると、その答えはイエスです。

呼び出しが実際には実装によって省略されている場合でも、コピー コンストラクターが使用されます。

于 2010-04-19T23:59:27.140 に答える
0

MSVC で /Wall を使用してコンパイルするとどうなるかを見たことがありますか? クラスについて次のように述べています。

nocopy.cc(21) : warning C4625: 'noncopyable' : copy constructor could not be
generated because a base class copy constructor is inaccessible
nocopy.cc(21) : warning C4626: 'noncopyable' : assignment operator could not be
generated because a base class assignment operator is inaccessible

GCC の救済策: から情報をコピーするためにできることを行うコピー コンストラクター(noncopyableおよび理想的には代入演算子!) を作成します。からのデータ。ただし、の定義を考えると、コピーするデータがないように見えるため、新しい関数のイニシャライザ リストに を単純に追加するだけで機能するはずです。noncopyable_basenoncopyable_basenoncopyablenoncopyable_basenoncopyable_basenoncopyable_base()noncopyable(const noncopyable &)

ただし、MSVC がプログラムについて述べたことに注意してください。また、T t()ではなくを使用するとT t = T()別の警告 (C4930)が MSVC によって生成されることに注意してください。ただし、GCC は警告を発行せずにどちらの方法でも喜んで受け入れます。

于 2010-04-20T00:21:17.977 に答える