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

invalid operands of types

by adnoctum 2010. 6. 12.

다음과 같은 에러가 났다.

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

[adnoctum@bioism analysis_code]$ make
g++ -c classify_GSE_into_cluster_file_pathway.cpp
classify_GSE_into_cluster_file_pathway.cpp: In function ‘int main(int, const char**)’:
classify_GSE_into_cluster_file_pathway.cpp:152: error: invalid operands of types ‘std::ofstream*’ and ‘const char [20]’ to binary ‘operator<<’

에러의 의미를 그대로 해석해 보면, main 함수에서 에러가 났는데, 그 내용은 "

binary operator인 'operator<<'에다가 std::ofstream* 과 const char[20] 타입의 operand를 넘겨 주었는데 그것은 잘못된 operand 이다

이다.

operator 라는 것은, 수학에서의 '연산자'를 의미하는 것이 프로그래밍에서 좀 더 자유롭게 사용되는데, 쉽게 예를 들면 + 나 -, *, / 같은 것이다. 즉, 어느 값을 받아서 다른 값을 반환하는 '함수'[각주:1]일 뿐이다. 따라서 두 수를 입력받아 큰 수를 반환하는 것이나 한 수를 입력받아서 100.0 에서부터의 거리를 반환하는 것, 또한, 그림 파일을 입력 받아서 그 파일의 위쪽 반만 잘라서 새로운 그림 파일을 반환하는 것도 '연산자'이고, 인터넷 주소를 입력받아서 그 주소에 해당하는 html 파일을 파일로 저장하는 것도 연산자이다.

그렇다면 binary operator란 무엇일까? 그것은, operator 가 작업을 하기 위해서는 입력으로 값이 두 개 주어져야 한다는 것이다. 수학에서 보면 곱하기, 나누기, 더하기, 빼기 등이 되겠지. 반면 unary operator는 입력으로 값을 한 개 받아서 결과값을 돌려 주는 연산자를 말하는데, 쉽게 말해 NOT, 역수(inverse) 등이 있겠다. 혹은 FFT 나 integral, differentiation 도 모두 단항연산자이다[각주:2]. 적분하라는 ∫ 도 '함수 f' 한 개를 입력받아서 그 함수의 적분된 형태를 반환하는 operator 이며, 인자로는 함수 1개를 필요로 하기 때문에 단항연산자인 것이다. 미분도 마찬가지.

이렇게 연산자에 입력으로 들어 가는 값들을 operand라 한다. 예를 들어, 5 + 5.9 , 라는 연산을 본다면, 연산자 + 는 5 와 5.9 라는 두 개의 operand를 사용하고 있는 것이다.

위의 에러 메세지는 operand 2개를 필요로 하는 binary operator인 operator<< 에 std::ofstream* 과 const char[20] 이 operand로 들어 갔는데, 그럴 수 없다는 내용이다. 에러가 난 코드를 보자.

  122 std::map<std::string,std::ofstream*> clusterpath2file;

  123 std::map<std::string,std::ofstream*>::iterator find_pos;

  124 

  125 // All pairs of cluster no. and pathways are to be stored in [all_cluster_pathway]

  126 // but only for the pathways in [critical_pathways].

  127 char temp[200] = "";

  128 int file_count = 1;

  129 std::string line;

  130 for(pos = gse_file_name_list.begin(); pos != gse_file_name_list.end(); pos++){

  131         //get_cluster_pathway_name(*pos, critical_pathways, &all_cluster_pathway, &content);

  132         std::cout << *pos << std::endl;

  133         std::ifstream ifile(pos->c_str());

  134         if(ifile.is_open() == false) continue;

  135         while(std::getline(ifile, line)){

  136                 if(line == "" || line[0] == '#') continue;

  137                 std::vector<std::string> v = split<std::string,string2string>(line, '\t');

  138                 if(v.size() != 10) continue;

  139                 if(critical_pathways.count(v[9]) == 0) continue;

  140                 std::string clusterpath = v[0] + ":" + v[1];

  141                 find_pos = clusterpath2file.find(clusterpath);

  142                 if(find_pos == clusterpath2file.end()){

  143                         sprintf(temp, (out_dir + "/path_cluster_corr_%d.txt").c_str(), file_count);

  144                         file_count++;

  145                         std::ofstream *out = new std::ofstream(temp);

  146                         *out << "INFO:SOURCE_CLUSTER_FILE_NAME:\t" << v[0] << std::endl;

  147                         *out << "INFO:PATHWAY_NAME:\t" << v[9] << std::endl;

  148                         *out << "!PATH_CLUSTER_CORR\t" << line << std::endl;

  149                         clusterpath2file.insert(std::make_pair<std::string,std::ofstream*>(clusterpath, out));

  150                 }

  151                 else{

  152                         find_pos->second << "!PATH_CLUSTER_CORR\t" << line << std::endl;

  153                 }

  154         }

  155 }



152 번째 줄을 보면 [find_pos->second] 이고, 123 번째 줄에 보면 [find_pos]는 다음과 같이 선언되어 있다.

  123 std::map<std::string,std::ofstream*>::iterator find_pos;


따라서 [find_pos->second]의 type은 std::ofstream* 이다. 언뜻 std::ostream::operator<< 는uniary operator 일 것 같아 에러 내용이 조금 애매했는데, 조금 찾아 보니, 다음과 같은 전역 함수가 존재한다.

ostream& operator<< (ostream& out, char c );
ostream& operator<< (ostream& out, signed char c );
ostream& operator<< (ostream& out, unsigned char c );

ostream& operator<< (ostream& out, const char* s );
ostream& operator<< (ostream& out, const signed char* s );
ostream& operator<< (ostream& out, const unsigned char* s );

아마도 gcc는 위 나열된 전역 함수들 중 bold로 해 놓은 것을 호출하려 했을테지, 하지만 첫 번째 operand가 ostream& 이 아니라 ostream* 였기 때문에 작업을 할 수 없었을 것이다. 결국 operand type으로 ostream* 와 const char* 를 받는 operator<< 는 그 어디에도 없었기 때문에 위와 같은 에러 메세지가 나왔던 것이다.

해결은, 매우 간단히,

  152                         (*find_pos->second) << "!PATH_CLUSTER_CORR\t" << line << std::endl;


로 하면 되겠지.


여기서, 잠시 다른 얘기 하나. 위에서 operator<< 를 저렇게 연달아 쓸 수 있는 것은, 사실 C++에서 reference로 return 하는 것이 유용하게 사용된 좋은 예이다.


std::ofstream o("temp.txt");
o << "a" << "\t" << 'b' << "\t" << 1 << std::endl;

은, 우선,

ostream& operator<< (ostream& out, const char* s );


함수에 의해 호출되는데, 이 때 ostream& out 에는 o 가, const char* s 에는 "a" 가 들어가서 반환값으로 ostream&가 반환되는데, 물론 입력으로 들어 갔던 out이 반환되어야 하기 때문에[각주:3], 위 경우 o 가 반환될 것이다. 따라서

o << "a"

의 반환값은 다시 o 이다! 여기서 다시 같은 방법으로 o와 "\t" 가 입력으로 들어가서 o 가 튀어 나온다. 그리고, 다시 'b'를 받기 위해 다음과 같은 전역함수가 실행될 것이다.

ostream& operator<< (ostream& out, char c );

물론 동일하게 ostream& out에는 o 가, char c 에는 'b'가 들어간 후, 반환되는 것은 역시나 o 그 자신이 반환되겠지. 이런 식으로 계속 reference가 반환되기 때문에 operator<< 를 연달아서 계속 쓸 수 있는 것이다.


  1. 따라서 동일한 값을 서로 다른 값으로 변환시키지 않는다는 함수의 조건만 만족시키면 모두 '연산자'가 될 수 있다. [본문으로]
  2. 동일한 함수를 적분하면 언제나 같은 함수가 나온다. 따라서 '적분'은 함수의 정의를 만족시키고, 따라서 '연산자'의 일종이다. [본문으로]
  3. 함수는 자기만의 stack 을 갖는데, 그 안에서 만들어진 변수의 reference를 반환하면 안된다는 것은 기본. 따라서 결국 parameter로 받은 참조를 반환해야만 할 것이다. [본문으로]

'컴퓨터 > 디버깅' 카테고리의 다른 글

segmentation fault의 원인  (1) 2010.09.30
cannot instantiate abstract class  (0) 2010.07.26
디버깅 공지사항  (0) 2010.05.25
no matching function for call to  (0) 2010.05.24
for 문의 동작 순서  (1) 2010.05.19