【问题标题】:Weird error - why is the compiler trying to call the copy constructor?奇怪的错误 - 为什么编译器试图调用复制构造函数?
【发布时间】:2011-12-31 16:55:21
【问题描述】:

我遇到了一些非常奇怪的错误。由于某种我不明白的原因,编译器似乎想要调用复制构造函数。

(118) std::map<int, layer> xs;
(119) xs.begin()->first; // error?!

layer 是不可复制的可移动字体。

class layer : public observable
{
    layer(const layer&);
    layer& operator=(const layer&);
public:
    layer(int index = -1);
    layer(layer&& other);
    layer& operator=(layer&& other);
   //...
};

由于某种原因,第 119 行导致编译器尝试调用 std::pair 的复制构造函数,为什么?

1>c:\program files (x86)\microsoft visual studio 10.0\vc\include\utility(131): error C2248: 'layer::layer' : cannot access private member declared in class 'layer'
1> ..\layer.h(55) : see declaration of 'layer::layer'
1> ..\layer.h(53) : see declaration of 'layer'
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\utility(129) : while compiling class template member function 'std::_Pair_base<_Ty1,_Ty2>::_Pair_base(const std::_Pair_base<_Ty1,_Ty2> &)'
1> with
1> [
1>     _Ty1=const int,
1>     _Ty2=layer
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\utility(174) : see reference to class template instantiation 'std::_Pair_base<_Ty1,_Ty2>' being compiled
1> with
1> [
1>     _Ty1=const int,
1>     _Ty2=layer
1> ]
1> ..\stage.cpp(119) : see reference to class template instantiation 'std::pair<_Ty1,_Ty2>' being compiled
1> with
1> [
1>     _Ty1=const int,
1>     _Ty2=layer
1> ]

我也尝试了以下方法,但同样失败。

(118) std::map<int, layer> xs;
(119) auto& t1 = *xs.begin();
(120) auto& t2 = t1.first; // error?!

这是怎么回事?

【问题讨论】:

  • 我为什么需要那个?我只是在读取成员变量“first”的值。
  • 好吧,也许是私有复制构造函数的问题,检查一下:stackoverflow.com/questions/1440287/… 第一个错误清楚地表明尝试访​​问 layer::layer 失败,因为它是私有的。
  • 您的代码示例使用 Visual Studio 2010 SP1 对我来说编译得很好(没有可观察的基类)。
  • 对空集合调用 begin 会产生 UB。你能发布一个真实的例子吗? SSCCE
  • 这并不能改变这不是一个真实例子的事实。如果您真的需要帮助,请发布一些独立的内容,而不仅仅是两三行代码。

标签: c++ visual-studio-2010 compiler-errors c++11


【解决方案1】:

这是模板错误的奇怪微妙之处之一。模板代码不是代码,它几乎更接近于生成代码的脚本语言。您甚至可以在函数中出现语法错误,在代码(直接或间接)使用该函数之前不一定会产生编译器错误。

在这种情况下,xs.first() 导致了 std::map::iterator 的生成,这也需要 std::pair 的生成。 std::pair 的默认实现有一个复制构造函数,编译失败。

您可以使用没有复制构造函数的模板特化 std::pair 来解决这个问题,但是您不能在地图中插入任何内容。 xs[0] = myLayer 创建 std::make_pair 并将其插入到您的地图中,这显然需要复制构建图层。

对此的典型解决方案是将您的类型更改为 std::map >。复制 shared_ptr 不会复制引用的对象。

【讨论】:

    【解决方案2】:

    这取决于你在哪里初始化这个成员,以及它的第一个成员。 如果您将其初始化为静态成员或在堆栈上而不调用构造函数,它将尝试调用默认构造函数(不带参数)并且由于您已将其设为私有而无法访问它。

    您必须为地图中的元素显式调用公共构造函数

    【讨论】:

    • 不是我,但默认构造函数是public
    【解决方案3】:

    你的例子:

    (118) std::map<int, layer> xs;
    (119) xs.begin()->first; // error?!
    

    http://www.cplusplus.com/reference/stl/map/

    xs.begin() Return iterator to beginning
    

    ... 它->首先; // 等同于 (*it).first(键值)

    因此

    xs.begin()->first;
    

    等价于

    pair<int,layer> piltmp = (*xs.begin());
    piltmp.first;
    

    等等。已在地图中创建了该对的副本。这涉及创建图层的副本。

    (如果地图持有指向图层的指针或自动指针,而不是图层本身,这将不是问题。)

    现在,如果 map::iterator::operator-> 返回 value_type 引用而不是 value_type,则不会发生这种情况。 IE。如果它返回一个左值而不是一个右值。似乎很奇怪,它没有,但我还没有通过标准。

    你可以通过这样做来解决它

    pair<int,layer>& piltmp = *xs.begin();
    return piltmp.first;
    

    (未测试。)

    【讨论】:

    • 不,xs.begin()-&gt;first 等同于 std::map&lt;int, layer&gt;::iterator it(xs.begin()); it-&gt;first;。迭代器指向std::pair&lt;first, layer&gt;,而不是layer
    • 很公平。第一个示例变为 piltmp = (*xs.begin(); piltmp.first。是的,在我的示例中这是一个错误,但重点仍然存在:显然存在该对的副本在地图中。并且该对包含一个图层,因此正在复制该图层。如果它是一个参考,一个左值,就没有问题。 /// 这提醒我:问题提出者可能会考虑使地图保持不变, 不是层,而是指向层的指针或 autoptrs。
    • 在您的示例中,您是对的,复制了一份。但是您的示例与xs.begin()-&gt;first 不同。它相当于pair&lt;int, layer&gt; &amp;tmp(*xs.begin()); tmp.first;。迭代器被复制,而不是迭代器指向的内容。迭代器上的operator*() 返回对映射中项目的引用,即std::pair&lt;int, layer&gt;。迭代器上的 operator-&gt;() 返回指向地图中项目的指针,而不是您在答案中提到的 value_type
    • 同意,这就是应该发生的事情。我只是在理解为什么要调用复制构造函数。你的正确解释是什么?
    • 正如问题下的cmets所提到的,其他人已经编译它没有问题,所以它可能是他的机器或代码上的东西。
    猜你喜欢
    • 2018-03-26
    • 2020-07-02
    • 2022-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多