2

更新以下のバグはVS2012で修正され、期待どおりにnoncopyable機能します

これは質問であり、情報を提供したり他の人に警告したりして、私と同じトラップに陥らないようにする方法でもあります。noncopyable基本クラス(ブーストのクラスなど)を使用しても、エクスポートされたクラスでは効果がないようです。 MSコンパイラ。これはMSにとって既知のバグですが、それを知っているプログラマーがたくさんいるとは思えません。ご想像のとおり、これにより、コンパイルすらしてはならないコードを記述できるため、非常に厄介なバグが発生する可能性があります。例(ここにコピーできないクラスのコード:)

dllプロジェクトの典型的なヘッダーファイル。次のコマンドでコンパイルし/D EXPORT_ITます。

#ifdef EXPORT_IT
  #define mydll __declspec( dllexport )
#else
  #define mydll __declspec( dllimport )
#endif    

class mydll CantCopyMe : private noncopyable
{
public:
  CantCopyMe();
  ~CantCopyMe();
};

mydll CantCopyMe MakeIt();

ソースファイル:

#include <iostream>

CantCopyMe::CantCopyMe()
{
  std::cout << "constructor" << std::endl;
}

CantCopyMe::~CantCopyMe()
{
  std::cout << "destructor" << std::endl;
}

CantCopyMe MakeIt()
{
  CantCopyMe x;
  return x; //oops... this sould not compile nor link but it does
}

アプリケーション:

int main()
{
  CantCopyMe x( MakeIt() );
}

出力:

constructor
destructor
destructor

1つのコンストラクタ、2つのデストラクタが呼び出されます。クラスにリソースが効果的に含まれている場合の問題を想像してみてください。

コンパイルは行うがすべきではない使用例を編集します。

CantCopyMe MakeIt()
{
  CantCopyMe x;
  return x;
}

void DoIt( CantCopyMe x )
{
  x.Foo();
}

void SomeFun()
{
  CantCopyMe x;
  DoIt( x );
}

その他の場合:CantCopyMe MakeIt(){return CantCopyMe(); //致命的なエラーC1001}

CantCopyMe GenerateIt()
{
  CantCopyMe x;
  return x;
}

CantCopyMe MakeIt()
{
  return GenerateIt(); //fatal error C1001
}

CantCopyMe MakeIt()
{
  CantCopyMe x;
  return CantCopyMe( x ); //fatal error C1001 + cl crashes
}

void DoSomething()
{
  CantCopyMe x;
  CantCopyMe y = x; //fatal error C1001 + cl crashes
}  

質問

  1. KBの記事では、今後のリリースでの修正について言及しています。誰かがこれがVS2010ですでに修正されているかどうかを確認できますか(またはおそらくVisual Studio 11プレビューで)?

  2. 何らかのエラーをトリガーするための回避策はありますか?return CantCopyMe()書き込みが内部コンパイラエラーをトリガーするという事実を(ab)使用してみましたが、MakeIt上記のような関数をコンパイルする場合にのみ条件付きでトリガーする方法を見つけることができませんでした。static_assertをコピー不可能なコピーコンストラクターに入れても、コンパイラーは呼び出されなくても常にコンパイルするため、それはカットされません。

4

2 に答える 2

2

1(VS2010の場合)に答えるために、VS2010(SP1を使用)で試してみましたが、正常にコンパイルされます。つまり、修正されていません。残念ながら、2011年にテストする必要はありません

2.へ。それを行う1つの方法は次のようになると思います。

  • コピー不可能なものから派生しなくなりました
  • 実装を提供せずに、CantCopyMeでコピーコンストラクタと代入演算子をプライベートとして宣言します)
class CantCopyMe 
{
public:
   //omitted for brevity...
private:
    CantCopyMe( const CantCopyMe& );
    const CantCopyMe& operator=( const CantCopyMe& );
};

これにより、説明する危険な状況を回避でき、VS2008でも機能するはずです。そして、適切な場所、つまりコピー不可能なクラス宣言で問題を解決します。

于 2011-09-20T10:15:55.347 に答える
1

少し異なる状況でこの同じ問題が発生しました。コピーできないメンバーが指定されたDLLエクスポートクラスがあります。DLLにエクスポートされたクラスには、明示的なコピーコンストラクターがなく、ヒープ上にそれ自体のコピーを返すCopyメソッドがあります。コピー不可能なメンバーが追加されたとき、コンパイラエラーはありませんでしたが、厄介なランタイムエラーがありました。私はそれを__declspec(dllexport)まで追跡し、それを削除すると、コピーを妨げる予期された正しいコンパイラエラーが発生したことを発見しました。この最小限の例を考えてみましょう。

#define API __declspec(dllexport)

class Inner
{
public:
    Inner() {}

private:
    Inner(const Inner&) {}
    Inner& operator=(const Inner&) { return *this; }
};

class API Outer
{
private:
    Inner i;

public:
    virtual Outer* Copy()
    {
        return new Outer(*this);
    }
};

これを最新のVS2010でコンパイルすると、次のようになりますerror C4716: 'Outer::Copy' : must return a value。Copy()をこれに変更すると:

virtual Outer* Copy()
{
    Outer* copy = new Outer(*this);
    return copy;
}

私は今、奇妙な警告だけを受け取ります:warning C4700: uninitialized local variable 'copy' used、そして実行時に厄介なクラッシュ。最後に、これを試してみてください。

virtual Outer* Copy()
{
    Outer tmp(*this);
    return nullptr;
}

コンパイラは確実にクラッシュします!これは、VS2010 SP1、80x86用のC++コンパイラバージョン16.00.40219.01にあります。

于 2011-10-06T21:31:01.937 に答える