다음과 같은 에러가 났다.

환경: 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
Posted by adnoctum
,