比较两个map :: iterators:为什么需要std :: pair的拷贝构造函数?

时间:2014-03-12 16:29:20

标签: c++ gcc c++11 clang std-pair

下面非常简单的代码在C ++ 98中编译和链接没有警告,但在C ++ 11模式下给出了一个难以理解的编译错误。

#include <map>

struct A {
    A(A& ); // <-- const missing
};

int main() {
    std::map<int, A> m;
    return m.begin() == m.end(); // line 9
}

-std=c++11的错误是,gcc版本4.9.0 20140302(实验性)(GCC):


ali@X230:~/tmp$ ~/gcc/install/bin/g++ -std=c++11 cctor.cpp 
In file included from /home/ali/gcc/install/include/c++/4.9.0/bits/stl_algobase.h:64:0,
                 from /home/ali/gcc/install/include/c++/4.9.0/bits/stl_tree.h:61,
                 from /home/ali/gcc/install/include/c++/4.9.0/map:60,
                 from cctor.cpp:1:
/home/ali/gcc/install/include/c++/4.9.0/bits/stl_pair.h: In instantiation of ‘struct std::pair’:
cctor.cpp:9:31:   required from here
/home/ali/gcc/install/include/c++/4.9.0/bits/stl_pair.h:127:17: error: ‘constexpr std::pair::pair(const std::pair&) [with _T1 = const int; _T2 = A]’ declared to take const reference, but implicit declaration would take non-const
       constexpr pair(const pair&) = default;
                 ^

with clang version 3.5(trunk 202594)

ali@X230:~/tmp$ clang++ -Weverything -std=c++11 cctor.cpp 
In file included from cctor.cpp:1:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/map:60:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/bits/stl_tree.h:63:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/bits/stl_algobase.h:65:
/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/bits/stl_pair.h:119:17: error: the parameter for this explicitly-defaulted copy constructor is const, but
      a member or base requires it to be non-const
      constexpr pair(const pair&) = default;
                ^
cctor.cpp:9:22: note: in instantiation of template class 'std::pair' requested here
    return m.begin() == m.end(); // line 9
                     ^
1 error generated.

我一直在查看bits/stl_tree.h中的代码,我不明白它为什么试图实例化std::pair

为什么在C ++ 11中需要std::pair的复制构造函数?


注意:上面的代码是从Equality operator (==) unsupported on map iterators for non-copyable maps

中提取的

这里有两个不幸的问题。

质量差的错误消息:第8行应该已经给出了编译错误,尽管错误消息只是抱怨第9行。在第8行获得错误将非常有帮助,理解真正的问题会容易得多。如果gcc / clang trunk中仍然存在此问题,我可能会提交错误报告/功能请求。

另一个问题是ecatmur写的内容。请考虑以下代码:

struct A {
    A() = default;
    A(A& ); // <-- const missing
};

template<class T>
struct B {
    B() = default;
    B(const B& ) = default;
    T t;
};

int main() {
  B<A> b;  
}

无法编译。即使复制构造函数在任何地方都不需要,它仍然被实例化,因为它在类的主体中是内联的默认值;这导致编译错误。这可以通过将复制构造函数移出类的主体来修复:

template<class T>
struct B {
    B() = default;
    B(const B& );
    T t;
};

template <class T>
B<T>::B(const B& ) = default;

一切都好。不幸的是,std::pair有一个默认定义的内联复制构造函数。

3 个答案:

答案 0 :(得分:3)

在这种情况下,std::pair的复制构造函数不是 ,但因为它是std::pair声明中的内联默认定义,所以它会自动实例化std::pair本身的实例化。

标准库可以提供复制构造函数的非内联默认定义:

template<class _T1, class _T2>
  struct pair
  {
// ...
    constexpr pair(const pair&);
// ...
  };
// ...
template<class _T1, class _T2>
constexpr pair<_T1, _T2>::pair(const pair&) = default;

然而,这不符合标准的严格字母(第20.3.2节),其中复制构造函数是默认的内联定义:

  constexpr pair(const pair&) = default;

答案 1 :(得分:2)

我想我在尝试减少错误后找到了它。首先,似乎不需要进行比较以使程序格式错误。然后,错误消息包含dtor,所以我尝试不实例化dtor。结果:

#include <map>

struct A {
    A(A& ); // <-- const missing
};

int main() {
    std::map<int, A>* m = new std::map<int, A>();
    // note: dtor not (necessarily?) instantiated
}

但是输出消息仍然包含,现在用于调用m的ctor的行:

  

错误:此显式默认的复制构造函数的参数是const,但成员或基础要求它是非const

 constexpr pair(const pair&) = default;

[dcl.fct.def.default] / 4

的提示
  

用户提供的显式默认功能(即,在其后明确默认)   第一个声明)是在明确默认的地方定义的; 如果将此功能隐式定义为已删除,则该程序格式错误

[强调我的]

如果我假设,[class.copy] / 11表示此ctor 应该定义为已删除,那么它被定义为立即删除 - 不仅仅是在使用它时。因此,不应要求实例化使程序格式错误。

答案 2 :(得分:0)

std::map使用std::pair存储键值对,其中键(第一个元素)为const

编译器错误与std::pair所需的复制构造函数有关,即使它没有被使用(我认为不是这样)。

必须生成

std::pair<int, A>。这是调用map :: begin时首先需要的。 由于没有为此类型指定显式复制构造函数,因此使用了隐式复制构造函数。

只有当 T的所有非静态成员(类型S)具有复制构造函数S :: S(const S&amp;)时,隐式构造函数才会有签名T :: T(const T&amp;) ;)(同样的要求必须适用于T的基类型复制构造函数)。 否则,将使用带有签名T :: T(T&amp;)的复制构造函数。

A的复制构造函数不符合此要求,因此std :: pair :: pair具有错误的STL签名,这需要T :: T(const T&amp;)。