[英]Comparing two map::iterators: why does it need the copy constructor of 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; ^
与clang版本3.5(主干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运算符(==)中提取的。
解
这里有两个不幸的问题。
质量差的错误消息:第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
有一个默认定义的内联拷贝构造函数。
在这种情况下, 不需要 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;
我想我在尝试减少错误后找到了它。 首先,似乎不需要进行比较以使程序格式错误。 然后,错误消息包含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表示,该构造函数应该定义为删除,然后根据立即删除它的定义-不仅在它的ODR使用。 因此,不应该要求实例化使程序格式错误。
std::map
使用std::pair
来存储键值对,其中键(第一个元素)是const
。
编译器错误与std::pair
所需的拷贝构造函数有关,即使它没有被使用(我认为它不是这样)。
必须生成std::pair<int, A>
。 这是调用map :: begin时首先需要的。 由于没有为此类型指定显式复制构造函数,因此使用了隐式复制构造函数。
只有当 T的所有非静态成员(类型S)都具有复制构造函数S :: S(const S&)时,隐式构造函数才会具有签名T :: T(const T&)(对于T的基础必须保持相同的要求)类型复制构造函数)。 否则,使用具有签名T :: T(T&)的复制构造函数。
A的复制构造函数不符合此要求,因此std :: pair :: pair具有错误的STL签名,这需要T :: T(const T&)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.