Подтвердить что ты не робот

Сравнение двух 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 имеет конструктор встроенной копии по умолчанию.

4b9b3361

Ответ 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 &).