什么是错误的错误,以及复制构造函数错误地调用Movable和Non-copyable成员的解决方法

Edd*_*tes 2 c++ compiler-errors visual-c++ move-semantics c++11

请考虑以下代码,该代码在VS2012中编译但在VS2010中因错误而失败

    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 ==========
Run Code Online (Sandbox Code Playgroud)

代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

任何人都可以指向提交给Microsoft的错误的链接,或者解释编译器错误实际上是什么,我试图理解代码的哪一部分确实困扰编译器.另外,我们有什么解决方法呢?

谢谢!

And*_*owl 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-nonqualified版本的临时函数.临时是一个prvalue.然后,根据上面的规则,调用的结果(对于构造函数的情况是临时的)用于直接初始化作为复制初始化目标的对象.在某些情况下,允许实现通过将中间结果直接构造到正在初始化的对象中来消除此直接初始化中固有的复制; 见12.2,12.8.

让我们假设为你的编译器的那一刻没有的Elid复制/移动(编译器是允许的,但不是必需的,这样做).

您的类模板没有定义任何移动构造函数,并且将选择隐式生成的复制构造函数来构造s临时对象,该临时对象是从初始化右侧的lambda构造的.

不幸的是,你的类有一个类型的成员变量Container,它是一个不可复制元素的容器.因此,隐式生成的复制构造的实例化将失败,这解释了您获得的错误.

您应该使用直接初始化:

const LazyValue<Container> s([]() -> Container {
  return ListOfRValues<Movable>()
    (Movable(new int) )
    (Movable(new int) )
    (Movable(new int) );
});
Run Code Online (Sandbox Code Playgroud)

现在让我们考虑编译器确实选择忽略复制/移动的情况.C++ 11标准中有关于此行为的要求,来自第12.8/32段:

当满足或将满足复制操作的省略标准时,除了源对象是函数参数这一事实,并且要复制的对象由左值指定,重载决策选择复制的构造函数是首先执行,好像对象是由右值指定的.如果重载决策失败,或者所选构造函数的第一个参数的类型不是对象类型的rvalue引用(可能是cv-qualified),则再次执行重载决策,将对象视为左值.[注意:无论是否发生复制省略,都必须执行此两阶段重载决策.如果未执行elision,它将确定要调用的构造函数,并且即使调用被省略,也必须可以访问所选的构造函数. - 尾注]

这里的关键术语是可访问的.隐式生成的复制构造函数的实例化不能成功,因为要复制的对象包含不可复制的子对象; 这必然会使复制构造函数无法访问,因为它永远无法实例化.因此,符合标准的编译器应拒绝编译代码,我相信这有资格作为VS2012中的错误.


PS:另外,请注意你违反了所谓的三条规则(除了有一个重载的复制赋值运算符,什么都不返回,而它应该返回*this).