使用关联容器map时遇到的一个问题(declared to take const reference, but implicit declaration would take non-const)
今天遇到一个问题,情形类似于这样:
1 #include <iostream> 2 #include <map> 3 4 using namespace std; 5 6 struct A { 7 A() = default; 8 //A(int a); 9 A(A& ) = default; 10 }; 11 12 13 14 15 int main() { 16 map<int, A> m; 17 A a; 18 m[1] = a; 19 /* 第18行报错: 20 constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const int; _T2 = A]’ declared to take const reference, but implicit declaration would take non-const 21 constexpr pair(const pair&) = default; 22 */ 23 }
实际上,如果没有写17,18行,问题在第16行就会报同样的错误
原因和解决方法如下:
1 #include <iostream> 2 3 using namespace std; 4 5 struct A { 6 A() = default; 7 //A(int a); 8 A(A& ) = default; // <-- const missing (如果声明为 A(const A& ) 就没有问题) 9 }; 10 11 template<class T> 12 struct B { 13 B() = default; 14 B(const B& ) = default; //B<T>::B(const B<T>&) [with T = A]’ declared to take const reference, but implicit declaration would take non-const 类模板中的默认复制构造函数是内联的 15 T t; 16 }; 17 18 /* template <class T> 19 B<T>::B(const B& ) = default; //如果不对A做改动,则需要把B的复制构函定义为非inline (这样做实际上只是掩耳盗铃。因为当我调用B的复制构函时仍会出问题) */ 20 21 int main() { 22 B<A> b; // 23 B<A> c(b); //采用将类模板的复制构函定义放到外面的方法时问题仍会出现 24 /* 第14行报错: B<T>::B(const B<T>&) [with T = A]’ declared to take const reference, but implicit declaration would take non-const 25 B(const B& ) = default; 26 问题原因:第22行虽然没有调用A的复制构函,但类模板的默认复制构函是inline的,所以在实例化的时候会默认生成B<T>的复制构函,而B的复制构函会将const T 作为入参调用A的复制构函,这与A的复制构函入参为非const冲突 27 */ 28 } 29 30 /* 31 1. 如果你定义了一个类模板,用别人的类来实例化时,如果别人的类复制构函入参不是const的(而你的类模板是const), 32 则可以把你的类模板默认复制构函的定义放到模板外面(因为默认是inline)(实际上这样做问题仍存在,只要用户代码调用B的复制构函时问题就会暴露), 33 或者干脆你的类模板的复制构函入参也声明为非const(这样才真正解决问题) 34 2. 如果你定义了一个类,用别人的类模板并将这个类作为模板参数时,如果别人的模板的复制构函入参是const的,则你的类的复制构函也必须是const 35 3. 当你自己的类用作关联容器的模板参数时,也必须把类复制构函的入参声明为const (因为map的元素类型是模板类pair<>,pair的复制构函是入参为const的内联函数,如同上面的struct B) 36 */