Неожиданная проблема компиляции с g++ -std= C++0x
У меня есть некоторые проблемы с компиляцией, когда элементы типа T возвращаются в вектор при компиляции с помощью g++ -std= C++0x.
Это минимальный пример:
#include <vector>
using namespace std;
class A {
public:
A() { }
A& operator=(A &orig) {
return *this;
}
};
int main(int argc, char **argv) {
A a;
vector<A> b;
A c = a; // This is fine
b.push_back(a); // This is not, but only when compiling with -std=c++0x!
return 0;
}
Он прекрасно компилируется с g++ -Wall -pedantic, но выдает эту ошибку при компиляции с g++ -Wall -pedantic -std= C++0x:
In file included from /usr/include/c++/4.4/vector:69,
from min.cpp:1:
/usr/include/c++/4.4/bits/vector.tcc: In member function ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, _Args&& ...) [with _Args = const A&, _Tp = A, _Alloc = std::allocator<A>]’:
/usr/include/c++/4.4/bits/stl_vector.h:741: instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = A, _Alloc = std::allocator<A>]’
min.cpp:20: instantiated from here
/usr/include/c++/4.4/bits/vector.tcc:314: error: no match for ‘operator=’ in ‘__position.__gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = A*, _Container = std::vector<A, std::allocator<A> >]() = ((const A&)((const A*)std::forward [with _Tp = const A&](((const A&)((const A*)__args#0)))))’
min.cpp:11: note: candidates are: A& A::operator=(A&)
In file included from /usr/include/c++/4.4/vector:61,
from min.cpp:1:
/usr/include/c++/4.4/bits/stl_algobase.h: In static member function ‘static _BI2 std::__copy_move_backward<true, false, std::random_access_iterator_tag>::__copy_move_b(_BI1, _BI1, _BI2) [with _BI1 = A*, _BI2 = A*]’:
/usr/include/c++/4.4/bits/stl_algobase.h:595: instantiated from ‘_BI2 std::__copy_move_backward_a(_BI1, _BI1, _BI2) [with bool _IsMove = true, _BI1 = A*, _BI2 = A*]’
/usr/include/c++/4.4/bits/stl_algobase.h:605: instantiated from ‘_BI2 std::__copy_move_backward_a2(_BI1, _BI1, _BI2) [with bool _IsMove = true, _BI1 = A*, _BI2 = A*]’
/usr/include/c++/4.4/bits/stl_algobase.h:676: instantiated from ‘_BI2 std::move_backward(_BI1, _BI1, _BI2) [with _BI1 = A*, _BI2 = A*]’
/usr/include/c++/4.4/bits/vector.tcc:308: instantiated from ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, _Args&& ...) [with _Args = const A&, _Tp = A, _Alloc = std::allocator<A>]’
/usr/include/c++/4.4/bits/stl_vector.h:741: instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = A, _Alloc = std::allocator<A>]’
min.cpp:20: instantiated from here
/usr/include/c++/4.4/bits/stl_algobase.h:561: error: no match for ‘operator=’ in ‘* -- __result = std::move [with _Tp = A&](((A&)(-- __last)))’
min.cpp:11: note: candidates are: A& A::operator=(A&)
Таким образом, кажется, что он не находит правильный оператор = A. Почему? Почему говорится with _Iterator = A*
когда я прохожу?
3 ответа
Назначаемое требование, налагаемое языковым стандартом на стандартные элементы контейнера, требует t = u
выражение должно быть действительным, даже если u
является постоянным объектом. Это требование было определено таким образом, начиная с C++98 (см. 23.1/4).
Вы нарушили это требование, поскольку ваш оператор присваивания не принимает const-объекты. Это сразу означает, что ваш класс A
не может использоваться как тип элемента контейнера.
Почему это работало в C++03, довольно неважно. Это сработало случайно. Из сообщения об ошибке очевидно, что реализация библиотеки C++0x использует некоторые специфические функции C++0x (например, std::move
), что делает вышеприведенное требование вступать в игру. Но в любом случае реализация C++03 (и даже реализация C++ 98) также может не скомпилироваться для вашего A
,
Ваш пример с A c = a;
не имеет значения, поскольку он вообще не использует оператор присваивания (почему он здесь?).
Чтобы исправить ошибку, вы должны либо принять параметр по константной ссылке, либо по значению.
Я совершенно уверен, что это функция безопасности. Типы с оператором копирования-назначения (или конструктором копирования), которые могут изменять правую часть, не безопасны для использования в стандартных контейнерах - примером этого является (сейчас не рекомендуется) std::auto_ptr
который ужасно сломается, если хранится в контейнере.
Старая реализация библиотеки C++03 допускала такой небезопасный код, но, по-видимому, они реализовали проверку во время компиляции в версии C++0x - возможно, в сочетании с включением перемещения контейнеров.
Стандартное определение оператора копирования-присвоения (раздел [class.copy]
):
Объявленный пользователем оператор копирования
X::operator=
является нестатической не шаблонной функцией-членом классаX
с ровно одним параметром типаX
,X&
,const X&
,volatile X&
или жеconst volatile X&
,
Но X&
а также volatile X&
варианты могут быть несовместимы с контейнерами при условии, что присвоение может быть сделано из R-значения RHS.
ПРИМЕЧАНИЕ: передача по значению, например X::operator=(X)
является фундаментальной частью идиомы копирования и обмена.