Сравнение двух map:: iterators: зачем нужен конструктор копирования std:: pair?
Самый простой код ниже компилируется и содержит ссылки без предупреждения в С++ 98, но дает непонятную ошибку компиляции в режиме С++ 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):
[email protected]:~/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)
[email protected]:~/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
.
Зачем нужен конструктор копирования std::pair
в С++ 11?
Примечание: приведенный выше код был извлечен из оператора Equality (==) неподдерживаемого на итераторах карты для не скопируемых карт.
Решение
Здесь есть две неудачные проблемы.
Сообщения об ошибках низкого качества: строка 8 уже должна давать ошибку компиляции, хотя сообщения об ошибках только жалуются на строку 9. Получение ошибки в строке 8 было бы весьма полезным, и понимание реальной проблемы было бы намного проще. Я, вероятно, отправлю отчет об ошибке/функции, если эта проблема все еще присутствует в магистрали gcc/clang.
Другая проблема - это то, что пишет 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
имеет конструктор встроенной копии по умолчанию.
Ответы
Ответ 1
Конструктор копирования 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;
Ответ 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
}
Но выходное сообщение все еще содержит, теперь для строки, где вызывается ctor m
:
error: параметр для этого явно построенного конструктора копии является const, но член или база требует, чтобы он был не const const
constexpr pair(const pair&) = default;
Какие намеки на [dcl.fct.def.default]/4
Предоставляемая пользователем функция с явно дефолтом (т.е. явно дефолт после ее первое объявление) определяется в том месте, где оно явно дефолтовано; , если такая функция неявно определена как удаленная, программа плохо сформирована.
[акцент мой]
Если, как я полагаю, [class.copy]/11 говорит, что этот ctor должен быть определен как удаленный, то он определяется как удаленный немедленно - не только при использовании odr. Поэтому не требуется создание экземпляра, чтобы программа была плохо сформирована.
Ответ 3
std::map
использует std::pair
для хранения пар ключ-значение, где ключ (первый элемент) равен const
.
Ошибка компилятора связана с требуемым конструктором копирования для std::pair
, даже если он не используется (что я не думаю, что это так).
std::pair<int, A>
должен быть сгенерирован. Это требуется сначала с вызовом map:: begin.
Поскольку для этого типа не указан явный конструктор копирования, используется неявный.
Неявный конструктор будет иметь подпись T:: T (const T &), только если все нестатические члены T (типа S) имеют конструкторы копирования S:: S (const S &) (то же требование имеет для хранения для конструкторов копирования типов базового типа).
В противном случае вместо этого используется конструктор копирования с сигнатурой T:: T (T &).
Конструктор копирования не выполняет это требование, поэтому std:: pair:: pair имеет неправильную подпись для STL, для которой требуется T:: T (const T &).