C++의 유일한(?) implicit const_cast

C++에는 (아마도 유일한) 묵시적 const_cast 가 있다. 바로,

char* str = “hello, world”;

이건 C++이 (최대한) C와의 하위 호환성을 가지려다 보니 나온 문제다. C 표준 라이브러리의 수많은 문자열 처리 함수들이 char* 을 인자로 받긴하지만, 의미 상으론 const char* 인 경우가 대부분이기 때문;

만약 const char* –> char* 을 명시적으로 바꿔야 한다면 많은 수의 C 코드가 C++ 컴파일러에서 컴파일되지 않게 된다. 그래서 이건 묵시적으로 const_cast<char*> 을 해 버린다.

하지만 모든 C 문자열 처리 함수가 const char* 의 의미로 동작하는건 아니다. 그래서 예전 버전의 Mozilla 에선 이와 관련한 버그가 있었다. 공유 문자열 버퍼를 strchr 같이 실제로 버퍼를 조작하는 함수에서 문제가 생기는 것 – const semantic이 깨지는 것.

여튼 지금 문제는 이게 아니라(…), 오전에 everclear 군이 제기한 코드;
아래와 같은 코드가 있다. 왜 성공적으로 컴파일 될까?

struct A {
int& a;
explicit A(int& _a) : a(_a) { }
A(const A& rhs) : a(rhs.a) { }
};

const A&인 rhs.a 는 const int& 가 되야 하지 않나? 비슷하게 int& a 대신 int* const a 여도 저 코드는 잘 동작한다; 대체 왜 이럴까?

Jinuk Kim
Jinuk Kim

SW Engineer / gamer / bookworm / atheist / feminist

Articles: 935

18 Comments

  1. 테스트 해 보니 struct의 const는 struct 자체가 const이지 내용까지 const가 되는 것은 아닌 것 같음.

    int* const a 경우는 const 대상이 value가 아니라 address라서 상관 없는 것 같고 const int* a로 하면 안될 듯?

    • 전 rhs.a 가 int* const 일 때도 const int* const 가 될 꺼라고 생각했죠(…). 형 말대로 address라 그런 듯

  2. C++의 const는 해당 객체나 배열이 “실제로” 차지하는 메모리 영역에 대한 비트 수준의 상수성만을 보장하는 것으로 알고 있습니다. 저 경우는 참조만 가져오는 것은 실제 포인터만 복사하이니 입력 인자로 주어진 객체의 비트 수준 상수성을 깨지 않기 때문에 허용되는 것 같고요. 그래서 포인터나 참조에 대한 상수성을 보장하려면 const getter 함수를 만들어야 하죠 OTL

  3. 읽으면서 자문 이후에 답을 쓰는 형식을 기대하면서 내렸는데
    자문으로 마무리 되는 포스팅이네요 ㅋㅋㅋ

  4. struct const의 멤버변수들은 const가 되지 않습니다. 위의 struct A로 예로 들면

    int a = 3;
    const A foo(a);
    foo.a = 4;

    위의 코드는 문제없이 작동합니다. foo는 상수지만, foo.a는 상수가 아니기 때문이죠.
    그래서 A(const A& rhs) : a(rhs.a) { } rhs.a가 상수가 아니기 때문에 컴파일이 됩니다.

    • struct const 의 말이 잘 이해가 안가긴하는데, summerlight 님과 같은 의미인건가요?
      A* const 같은 의미라면 말이 되는데, 단순히 const 인 구조체의 멤버는 const는 아니란 말인지 좀 모호하네요;

      만약 제가 잘못 이해한거라면;

      struct A {
      int x;
      };

      const A a;
      a.x = 0; // error

      const struct 의 멤버들은 const 라 (summerlight 님의 말대로 메모리 레벨에서), 코드가 컴파일 되지 않습니다.

  5. 그닥 deep casting 해주진 않는듯? 그래서 const & A 인 경우에는 A::do_somthing(..) const { … } 이건 호출 가능하고, A::do_something(..) { … } 이건 불가능하고 – 이런 제한이 붙는거 아닐까? 저렇게 깊게 const casting 해주면 이런거 필요 없을 것 같은데

  6. C99 3.9.3.3에 이런 말이 있군요. Each non-static, non-mutable, non-reference data member of a const-qualified class object is const-qualified, .

    static/mutable 멤버는 당연할텐데, reference가 그럴 거라고는 상상 못했네요.

    • summerlight 님 말과 같은 얘기가 되겠네요. 결국 C++이 만들어주는 상수성(const-ness)의 개념이 단순한 메모리 상수성이라 표준도 일단 저러한듯 하군요;
      mutable은 변형이니 약간 다른 얘기지만요(…).

Leave a Reply