C++의 참조에 대해

Twitter icon류광, 2007-10-20 19:10
별칭(alias)으로서의 참조(reference)에 대해(C++).

TaocpHelp에도 참여하셨던 xevious7님의 레퍼런스(Reference)에 대한 고찰을 읽다가 한 가지 눈에 띈 것이 있어서 글을 씁니다.

"참조자(reference)는 객체에 대한 다른 이름이라고 두리뭉실하게 정의한다."라고 인용하셨는데, 흔히 틀리는 단어인 '두리뭉실'이 눈에 걸렸습니다. 한국어판을 확인해 보니 두리뭉실이 아닌 두루뭉술이라고 정확하게 되어 있습니다. 그런데 그게 중요한 것은 아니고, 원서에 나온 참조의 정의는 별로 두루뭉술하지 않습니다. 원문은 그냥 "A reference is an alternative name for an object."입니다.

원문을 "참조라는 용어(또는 개념)는 객체라는 용어(또는 개념)의 또 다른 이름이다"라고 해석한다면 좀 두루뭉술하기도 하겠습니다. 그러나 이 경우에는 부정관사 a(an)을 살리는 것이 중요합니다. 장황하게 옮긴다면 "어떤 특정한 하나의 참조는 어떤 특정한 하나의 객체에 대한 어떤 특정한 하나의 다른 이름이다"가 되겠죠. "어떤 특정한 하나의"를 구체적인 사례로 치환하면 의미가 명확해 집니다. 예를 들어 int& rr = ii;에서 rrii(가 대표하는 어떤 객체)의 다른 이름인 것이죠.

간단히 말해서 "참조는 별칭(alias)"입니다(그런 차원에서 동사로서의 reference는 참조하다, 나타내다 보다는 '지칭하다'라고 옮기는 게 더 나을 수 있겠습니다). "참조는 별칭"이라는 관점에서 5.5절의 내용을 읽으면 모든 것, 이를테면 포인터와 참조의 차이, 참조에 대한 최적화 등등이 명확해집니다.

이 부분을 한국어판 감수 할 때 점검했어야 하는데 놓쳐버렸습니다. 곽용재님의 재치있는 표현이겠거니 하고 넘어 갔었을 수도 있겠지만, 사실 제가 프로그래밍 언어에서 '이름'이 가지는 의미를 당시에는 제대로 파악하지 못했던 것 같습니다. 이에 대해서는 나중에 좀 더 글을 써 보겠습니다.

최적화에 대해 잠깐 이야기하자면, 참조는 별칭이고 RTTI가 관여하지 않는다고 할 때 C++ 프로그램 안의 모든 이름은 전적으로 컴파일 시점의 속성들이므로, 참조 변수들은 실행 시점 코드와는 전혀 무관할 수 있습니다.

한 예로 성분별 접근과 배열 접근을 모두 지원하는 Point 구조체를 구현하는 문제를 들어 보겠습니다. OpenGL에는 하는 일은 같되 x, y, z 좌표를 따로 받는 버전과 x, y, z 좌표들을 담은 배열의 포인터를 받는 버전을 제공하는 함수들이 있습니다. 예를 들면:

void glVertex3d( GLdouble x,
        GLdouble y,
        GLdouble z );

void glVertex3dv( const GLdouble *v );

하나의 Point 구조체로 이 둘을 모두 지원하고자 할 때(즉 glVertex3d(p.x, p.y, p.z);glVertex3dv(p);) 흔히 공용체+익명 구조체를 사용하거나 연산자 중복적재로 첫 성분(이 경우 x)의 주소를 넘겨주는 방법을 사용하는데, 둘 다 C++ 표준이 보장하는 방식이 아닙니다. 소위 "정의되지 않는 행동"을 일으키지 않는 유일한(제가 아는 한) 방법은 다음과 같은 방식입니다.

struct Point
{
  GLdouble& x;
  GLdouble& y;
  GLdouble& z;
  GLdouble data[3];
  Point(GLdouble x_, GLdouble y_, GLdouble z_) 
    : x(data[0]), y(data[1]), z(data[2])
  {
    data[0] = x_; 
    data[1] = y_; 
    data[2] = z_; 
  }
  operator GLdouble*() { return &data[0]; }
  // ... 기타 등등 ... 
};

컴파일러가 참조를 상수 포인터로 구현한다면 포인터 세 개 만큼의 메모리가 낭비됩니다. 그러나 x, y, z가 단지 data의 세 원소에 대한 또 다른 이름이라는 점에 근거해서 똑똑하게 컴파일한다면 기대했던 대로 GLdouble 세 개만 할당할 것입니다.

사족: 참조 변수의 메모리 사용 여부에 대해 표준이 특별하게 요구하는 것이 없기 때문에, 성분별 접근과 배열 접근을 동시에 제공하면서도 공간이 낭비되지 않는 구조체를 "표준이 보장하는" 방식으로 만드는 방법은 현재로서는 없는 것 같습니다. 혹시 가능한 방법이 있다면 알려주세요~

태그: 번역 프로그래밍 C++

comments powered by Disqus

예전 댓글(읽기 전용)