본문 바로가기
컴퓨터/디버깅

discards qualifiers 에러 : STL의 map의 [] 연산자의 반환값

by adnoctum 2011. 4. 14.


   STL의 map 의 [] 연산자는 반환값이 value 값으로 넣어 준 type T에 대한 참조형이다. 만약 key 로 입력한 값에 해당하는 value 가 없을 때는 value 값의 type T에 대한 default 생성자가 호출된 값을 입력하고, 그 값에 대한 참조를 반환하게 된다. 따라서 만약 key 가 없었다면 반드시 map 의 크기가 1 이 증가하게 된다.

다음과 같은 에러가 났다.

작업 환경: GCC 4.1.2 20080704 (Red Hat 4.1.2-46) on CentOS 5.4

[adnoctum@bioism bc_tf_genesets]$ g++ ~/LJSLibrary/library/util.o remove_zero_gene_reorder_gsm.cpp -o remove_zero_gene_reorder_gsm
remove_zero_gene_reorder_gsm.cpp: In member function ‘bool dfs_comparator::operator()(const std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, const std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&) const’:
remove_zero_gene_reorder_gsm.cpp:23: error: passing ‘const std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, double> > >’ as ‘this’ argument of ‘_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = std::basic_string<char, std::char_traits<char>, std::allocator<char> >, _Tp = double, _Compare = std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, _Alloc = std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, double> >]’ discards qualifiers

뒤에 에러가 더 있는데, 일단 이것만 본다. 문제는 위에서 표시한 'discards qualifiers' 라는 말인데, 이것은 qualifier 중 몇 개가 제대로 쓰일 수 없었기 때문에 버려야만 한다는 의미이다. 즉, 내가 어떠한 지시자를 사용했지만 논리상 그것을 사용할 수 없기 때문에 버리게 된다는 의미이다. 무엇일까?

사실 위 에러는 저 메세지만 보고는 그 원인을 알기는 조금 어려운데, 에러가 난 위치를 보면 쉽게 알 수 있다. 다음과 같다.


에러가 난 23 번째 줄은 map 의  [ ] 연산자를 접근하고 있다. 그런데 여기서 왜 qualifier를 버리게 된다는 에러가 났을까? 또한, 여기서 버려지는 qualifier는 무엇일까?

map의 [ ] 연산자는 만약 key 값이 없을 경우 value type에 해당하는 객체의 default constructor 를 호출해서 객체를 생성하고 그것을 집어 넣는다고 했다. 따라서 만약 위의 경우, std::string 으로 넘겨 진 key 가 없을 경우 double 형 값 (0.0) 을 그 key에 할당을 하게 될 것이다. 위의 경우 [_dfs]라는 변수에 그 값이 추가되는 것이다. 그런데, 문제는 operator()(~~) 가 const member function 으로 선언이 되었다. 즉, operator() 는 member 변수 중 그 어느 변수도 변경하지 않아야 한다. 그런데, 위의 경우 [_dfs] 라는 map 에 [gsmID1] 이나 [gsmID2] 에 해당하는 값이 없을 경우, _dfs.insert(std::make_pair<std::string,double>(gsmID1, 0)); 과 같은 작업이 일어나게 된다. 바로 이 부분에서 member variable 인 [_dfs]에 수정이 일어나게 되고, 이것은 operator() 를 const 로 지정한 것에 위배된다. 바로 이런 이유 때문에 const 라는 qualifier 가 버려질 수밖에 없게 되는 것이다.

버려진다기보다는 버릴 수밖에 없는 논리적 오류를 맞딱뜨린 것이지. 만약 위와 같은 경우라면 어떻게 해야할까? 지금 저 dfs_comparator 형 변수를 std::sort 함수의 세 번째 인자에 function object 로 넘기려 하고 있다. 따라서dfs_comparator::operator() 는 strict weak ordering 을 만족시키면 되므로 다음과 같이 key 가 없었던 객체가 key 가 있었던 객체보다 작다고 정의하자.


그러면 strict weak ordering 을 만족하면서도 const qualifier 를 살리게 되기 때문에 논리적 오류도 없고, compile 에러도 없게 된다.
+ 끄 ~ 읏 :)




위에서 왜 strict weak ordering 이 만족되는지 조금 의아할 수도 있다. 간단한데, parameter 로 넘어 온 [op1] 이나 [op2] 중 어느 하나에 대한 gsmID 가 없을 경우 그것이 '작은 것'이 되므로 문제 없고, 둘 다 값이 있으면 결국 double 형 value 가 비교되므로 문제 없다. 약간 난해한 부분은 둘 다 gsmID 에 대한 value 가 없는 경우인데, 이 경우 false; 가 반환된다. 여기서 strict weak ordering 이 빛을 발휘하게 되는데, 다음과 같은 경우를 생각해 보자.

std::vector<std::string> a;
// [a] 가 어떤 값으로 채워짐.
std::vector<std::string> b;
// [b] 가 어떤 값으로 채워짐.
std::map<std::string,double> dfs;
// [dfs]가 어떤 값으로 채워짐. 그러나 [dfs]에는 a[0]과 b[0] 에 대한 value가 존재하지 않음.

dfs_comparator dc(dfs);
bool comp1 = dc.operator()(a, b);  // --- (1)
bool comp2 = dc.operator()(b, a);  // --- (2)

위 코드에서 (1) 과 (2) 에서 [comp1] 과 [comp2] 는 모두 false; 가 반환된다. 따라서 a 와 b는, strict weak ordering 에 의해, '같다', 라고 정의된다. 그리고, 이런 경우, gsmID 가 있는 애들보다 작다고 정의했으므로 std::sort 에 의해, 이런 애들은 가장 작은 쪽으로 한 곳에 몰리게 된다.


참고: strict weak ordering