C++의 참조에 대해

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

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

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

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

간단히 말해서 "참조는 별칭(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 세 개만 할당할 것입니다.

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

top
트랙백 1 : 의견 # + 5

Trackback Address :: http://occamsrazr.net/tt/trackback/166

  1. Tracked from 이름없는 블로그 2008/06/06 23:36 DELETE

    Subject: 항목 20: '값에 의한 전달'보다는 '상수객체 참조자에 의한 전달'방식을 택하는 편이 대개 낫다.

    '값에 의한 전달' 이란 무엇을 뜻하는 것일까? 그 값을 복사하여, 복사된 값을 함수에 전달하는것을 뜻한다. '상수객체 참조자에 의한 전달'이란 무엇을 뜻하는 것일까? 포인터나 레퍼런스 즉 *..
comments powered by Disqus

(2013년 11월 10일자로 블로그에도 DISQUS 시스템을 도입했습니다. 기존 의견의 수정, 삭제, 댓글 추가는 여전히 가능합니다.)

  1. kwak101 2007/10/21 09:02 PERMALINKMODIFY/DELETE REPLY

    지적 감사합니다. 제가 매우 두루뭉술하게 번역했군요. ^^

    그 당시에는, 참조자의 구현이 구현환경마다 달라질 수 있다는 생각으로 그 표현을 넣은 듯합니다.

    받아들이는 측에 따라서는 조금 더 큰 모호함을 받을 수도 있을 것이라 생각합니다. 그렇게 본다 하더라도 크게 모호하다는 생각은 들지 않아서, 당장 정오표에 반영할지에 대해서는 다소 미온적인 입장입니다.

  2. 류광 2007/10/22 19:34 PERMALINKMODIFY/DELETE REPLY

    문제는 책이 책이다보니(또 저자가 저자다보니) 예를 들어 "BS 스스로도 C++의 참조의 정의가 명확하지 않다고 인정했다" 같은 식으로 인용을 당할 수도 있습니다 :) 여러 프로그래밍 언어들이 C++을 공격함으로써 자신을 홍보하는만큼 신경이 좀 쓰이는 문제지요. (그래서인지 BS는 FAQ에 "Did you really say that?"이라는 항목을 두고 있습니다: http://www.research.att.com/~bs/bs_faq.html#really-say-that )

  3. kwak101 2007/10/26 23:22 PERMALINKMODIFY/DELETE REPLY

    말씀 듣고 보니 그럴 수 있는 여지도 있다는 생각이 팍 드는데요? 정오표 수정을 고려해 봐야 겠습니다. 다음 주까지는 마무리를 짓지요.

  4. kwak101 2007/11/03 10:59 PERMALINKMODIFY/DELETE REPLY

    해당 사항을 정오표에 반영했습니다. ^^

    http://kwak101.pe.kr/wiki/wiki.php/TCppPL%EC%A0%95%EC%98%A4%ED%91%9C

  5. 류광 2007/11/03 20:23 PERMALINKMODIFY/DELETE REPLY

    수고하셨습니다~