2

Please consider the code below, which compiles in VS2012 but fails in VS2010 with the error

    1>------ Build started: Project: testconstinit, Configuration: Debug Win32 ------
1>  testconstinit.cpp
1>c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory(48): error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
1>          with
1>          [
1>              _Ty=int
1>          ]
1>          c:\program files (x86)\microsoft visual studio 10.0\vc\include\memory(2347) : see declaration of 'std::unique_ptr<_Ty>::unique_ptr'
1>          with
1>          [
1>              _Ty=int
1>          ]
1>          c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory(197) : see reference to function template instantiation 'void std::_Construct<std::unique_ptr<_Ty>,const std::unique_ptr<_Ty>&>(_Ty1 *,_Ty2)' being compiled
1>          with
1>          [
1>              _Ty=int,
1>              _Ty1=std::unique_ptr<int>,
1>              _Ty2=const Movable &
1>          ]
1>          c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory(196) : while compiling class template member function 'void std::allocator<_Ty>::construct(std::unique_ptr<int> *,const _Ty &)'
1>          with
1>          [
1>              _Ty=Movable
1>          ]
1>          c:\program files (x86)\microsoft visual studio 10.0\vc\include\vector(421) : see reference to class template instantiation 'std::allocator<_Ty>' being compiled
1>          with
1>          [
1>              _Ty=Movable
1>          ]
1>          c:\program files (x86)\microsoft visual studio 10.0\vc\include\vector(481) : see reference to class template instantiation 'std::_Vector_val<_Ty,_Alloc>' being compiled
1>          with
1>          [
1>              _Ty=Movable,
1>              _Alloc=std::allocator<Movable>
1>          ]
1>          c:\users\zadirion\documents\visual studio 2010\projects\testconstinit\testconstinit\testconstinit.cpp(34) : see reference to class template instantiation 'std::vector<_Ty>' being compiled
1>          with
1>          [
1>              _Ty=Movable
1>          ]
1>          c:\users\zadirion\documents\visual studio 2010\projects\testconstinit\testconstinit\testconstinit.cpp(81) : see reference to class template instantiation 'LazyValue<T>' being compiled
1>          with
1>          [
1>              T=Container
1>          ]
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

The code:

#include "stdafx.h"
#include <vector>
#include <memory>
#include <functional>
#include <deque>


using namespace std;



typedef std::unique_ptr<int> Movable;
typedef vector<Movable> Container;

typedef vector<Movable> (*MakeType)();


template <class T, class Initializer = function<T(void)> >
struct LazyValue
{
  LazyValue(Initializer aInit) : mInit(aInit) {}


  void Init() const
  {
    m = mInit();
  }

private:

  mutable T m; // <-- compiler error at this line
  Initializer mInit;

  LazyValue operator=(const LazyValue & aOther)
  {

  }
};

template <class T>
struct GenericList
{
    std::deque<T> mValues;

    GenericList(){}

    GenericList & operator()(T && aValue)
    {
        mValues.push_back(std::move(aValue));
        return *this;
    }

    template <class Container>
    operator Container()
    {
        auto it = mValues.begin();
        auto endIt = mValues.end();

        Container c;

        for ( ; it != endIt; it++ )
        {
            c.push_back(std::move(*it));
        }
        return std::move(c);
    }
};

template <class T>
GenericList<T> ListOfRValues()
{
    return GenericList<T>();
}

int _tmain(int argc, _TCHAR* argv[])
{

  const LazyValue<Container> s = []()->Container{
      return ListOfRValues<Movable>()
        (Movable(new int) )
        (Movable(new int) )
        (Movable(new int) );
  };

  return 0;
}

Can anyone point with a link to the bug submitted to Microsoft maybe, or an explanation on what the compiler bug is actually, I am trying to understand which part of the code exactly is troubling the compiler. Also, what workaround do we have for this?

Thank you!

4

1 に答える 1

5

このコードはコンパイルしないでください。

問題は、コピー初期化を使用しているという事実にあります。これには、(コンパイラがそれを除外していない場合)タイプの一時オブジェクトの構築が必要になる場合があります。LazyValue<Container>このオブジェクトは、初期化されたオブジェクトに移動されますs

C++11標準のパラグラフ8.5/14から:

フォームで発生する初期化

T x = a;

引数の受け渡し、関数の戻り、例外のスロー(15.1)、例外の処理(15.3)、および集約メンバーの初期化(8.5.1)は、コピー初期化と呼ばれます。[注:コピー初期化により、移動(12.8)が呼び出される場合があります。—エンドノート]

さらに、8.5 / 16項によると:

[...]それ以外の場合(つまり、残りのコピー初期化の場合)、ソースタイプから宛先タイプに、または(変換関数が使用されている場合は)その派生クラスに変換できるユーザー定義の変換シーケンスが列挙されます。 13.3.1.4で説明されているように、過負荷解決(13.3)によって最適なものが選択されます。変換を実行できないか、あいまいな場合、初期化の形式が正しくありません。選択された関数は、初期化子式を引数として呼び出されます。関数がコンストラクターの場合、呼び出しは宛先タイプのcv非修飾バージョンの一時的なものを初期化します。一時的なものはprvalueです。次に、呼び出しの結果(コンストラクターの場合は一時的なもの)を使用して、上記のルールに従って、コピー初期化の宛先であるオブジェクトを直接初期化します。場合によっては、実装は、初期化されるオブジェクトに直接中間結果を構築することにより、この直接初期化に固有のコピーを排除することが許可されます。12.2、12.8を参照してください。

今のところ、コンパイラーがコピー/移動を削除しないと仮定しましょう(コンパイラーは削除することを許可されていますが、必須ではありません)。

クラステンプレートはmoveコンストラクターを定義せずs、初期化の右側のラムダから構築された一時オブジェクトからオブジェクトを構築するために、暗黙的に生成されたコピーコンストラクターが選択されます。

残念ながら、クラスには、コピー不可能な要素Containerのコンテナであるタイプのメンバー変数があります。したがって、暗黙的に生成されたコピー構築のインスタンス化は失敗します。これは、発生しているエラーを説明しています。

代わりに直接初期化を使用する必要があります。

const LazyValue<Container> s([]() -> Container {
  return ListOfRValues<Movable>()
    (Movable(new int) )
    (Movable(new int) )
    (Movable(new int) );
});

ここで、コンパイラがコピー/移動を削除することを選択した場合を考えてみましょう。C ++ 11標準には、この動作に関する要件があり、12.8/32項に基づいています。

ソースオブジェクトが関数パラメーターであり、コピーされるオブジェクトが左辺値で指定されていることを除いて、コピー操作の省略基準が満たされている、または満たされる場合、コピーのコンストラクターを選択するためのオーバーロード解決は次のようになります。最初は、オブジェクトが右辺値で指定されているかのように実行されます。オーバーロード解決が失敗した場合、または選択したコンストラクターの最初のパラメーターのタイプがオブジェクトのタイプへの右辺値参照ではない場合(おそらくcv修飾)、オブジェクトを左辺値と見なして、オーバーロード解決が再度実行されます。[注:この2段階の過負荷解決は、コピーの省略が発生するかどうかに関係なく実行する必要があります。省略が実行されない場合に呼び出されるコンストラクターを決定し、選択したコンストラクターにアクセスできる必要がありますたとえ通話が省略されたとしても。—エンドノート]

ここでの重要な用語はアクセス可能です。コピーされるオブジェクトにコピー不可能なサブオブジェクトが含まれているため、暗黙的に生成されたコピーコンストラクターのインスタンス化は成功しません。これは、インスタンス化できないため、必然的にコピーコンストラクタにアクセスできなくなります。したがって、準拠するコンパイラはコードのコンパイルを拒否します。これはVS2012のバグと見なされます。


PS:また、いわゆる3のルールに違反しているという事実に注意してください(何も返さないオーバーロードされたコピー代入演算子があることを除けば、おそらく返されるはず*thisです)。

于 2013-02-19T21:08:15.867 に答える