const_cast

const_cast

constvolatile 한정을 바꿔야 할 때가 있어요. 그런 cv 한정 변환을 전용으로 하는 캐스트가 const_cast예요. 이 페이지에서 const_cast가 무엇을 할 수 있고, 무엇을 조심해야 하는지 살펴볼게요.

출처: cppreference

본문

구문 (Syntax)

const_cast< target-type >( expression )

target-type 타입의 값을 반환해요.

설명 (Explanation)

const_cast로는 다음 변환만 할 수 있어요.

  • expression이 널 포인터 값이면 결과도 널 포인터 값이에요.
  • expression이 널 멤버 포인터 값이면 결과도 널 멤버 포인터 값이에요.
  • expression이 객체를 가리키면 결과는 같은 객체를 가리켜요.
  • expression이 객체의 끝을 지나치면 결과는 같은 객체의 끝을 지나쳐요.
  • expression이 데이터 멤버를 가리키면 결과는 같은 데이터 멤버를 가리켜요.

expression이 prvalue여도 임시 구체화(temporary materialization)는 수행되지 않아요. (since C++17)

  • 타입 T1의 lvalue는 const_cast<T2&>로 타입 T2의 lvalue로 명시적으로 변환할 수 있어요.
  • 타입 T1의 glvalue는 const_cast<T2&&>로 타입 T2의 xvalue로 명시적으로 변환할 수 있어요.
  • T1이 클래스 또는 배열 타입이면 타입 T1의 prvalue는 const_cast<T2&&>로 타입 T2의 xvalue로 명시적으로 변환할 수 있어요. (since C++11)

결과 참조는 원래 객체를 가리켜요. (until C++17)

expression이 glvalue면 결과 참조는 원래 객체를 가리켜요. 그렇지 않으면 결과 참조는 구체화된 임시 객체를 가리켜요. (since C++17)

모든 캐스트 표현식과 마찬가지로 결과는 다음과 같아요.

  • target-type이 lvalue 참조 타입이거나 함수 타입에 대한 rvalue 참조(since C++11)면 lvalue.
  • target-type이 객체 타입에 대한 rvalue 참조면 xvalue. (since C++11)
  • 그 외에는 prvalue.

constness 벗겨내기 (Casting away constness)

서로 다른 두 타입 T1T2에 대해, T1에서 T2로의 변환은 다음과 같은 경우 constness를 벗겨내는(casts away constness) 변환이에요. 즉 T2의 한정 분해(qualification-decomposition)가 "cv2_0 P2_0 cv2_1 P2_1 ... cv2_n−1 P2_n−1 cv2_n U2" 형태인데, T1"cv2_0 P1_0 cv2_1 P1_1 ... cv2_n−1 P1_n−1 cv2_n U1"(같은 cv 구성요소, 다른 P 구성요소와 U 구성요소)로 변환하는 한정 변환이 없는 경우에요.

T1* 타입의 prvalue에서 T2* 타입으로의 캐스트가 constness를 벗겨낸다면, T1 타입의 표현식에서 T2에 대한 참조로의 캐스트도 constness를 벗겨내요.

constness를 벗겨내는 데는 const_cast만 사용할 수 있어요.

"constness 벗겨내기"는 "volatility 벗겨내기"를 암시해요. 한정 변환이 volatility를 벗겨낼 수 없기 때문이에요.

참고 (Notes)

함수 포인터와 멤버 함수 포인터는 const_cast의 대상이 아니에요.

const_cast는 실제로 const 객체를 가리키는 비-const 타입의 참조나 포인터, 또는 실제로 volatile 객체를 가리키는 비-volatile 타입의 참조나 포인터를 만드는 것을 가능하게 해요. 비-const 접근 경로를 통해 const 객체를 수정하거나, 비-volatile glvalue를 통해 volatile 객체를 참조하면 정의되지 않은 동작이 돼요.

키워드 (Keywords)

const_cast

예제 (Example)

#include <iostream>

struct type
{
    int i;
    
    type(): i(3) {}
    
    void f(int v) const
    {
        // this->i = v;                 // compile error: this is a pointer to const
        const_cast<type*>(this)->i = v; // OK as long as the type object isn't const
    }
};

int main()
{
    int i = 3;                 // i is not declared const
    const int& rci = i;
    const_cast<int&>(rci) = 4; // OK: modifies i
    std::cout << "i = " << i << '\n';
    
    type t; // if this was const type t, then t.f(4) would be undefined behavior
    t.f(4);
    std::cout << "type::i = " << t.i << '\n';
    
    const int j = 3; // j is declared const
    [[maybe_unused]]
    int* pj = const_cast<int*>(&j);
    // *pj = 4;      // undefined behavior
    
    [[maybe_unused]]
    void (type::* pmf)(int) const = &type::f; // pointer to member function
    // const_cast<void(type::*)(int)>(pmf);   // compile error: const_cast does
                                              // not work on function pointers
}

출력:

i = 4
type::i = 4

결함 보고 (Defect reports)

다음 동작 변경 결함 보고는 이전에 발표된 C++ 표준에 소급 적용됐어요.

DR 적용 대상 발표된 동작 올바른 동작
CWG 1965 C++11 const_cast가 rvalue 참조를 배열 prvalue에 바인딩할 수 없었음 그런 참조를 바인딩할 수 있게 됨
CWG 2879 C++17 포인터 prvalue 피연산자가 구체화됨 구체화되지 않음

참고 문헌 (References)

  • C++23 표준 (ISO/IEC 14882:2024): 7.6.1.11 Const cast [expr.const.cast]
  • C++20 표준 (ISO/IEC 14882:2020): 7.6.1.10 Const cast [expr.const.cast]
  • C++17 표준 (ISO/IEC 14882:2017): 8.2.11 Const cast [expr.const.cast]
  • C++14 표준 (ISO/IEC 14882:2014): 5.2.11 Const cast [expr.const.cast]
  • C++11 표준 (ISO/IEC 14882:2011): 5.2.11 Const cast [expr.const.cast]
  • C++98 표준 (ISO/IEC 14882:1998): 5.2.11 Const cast [expr.const.cast]
  • C++03 표준 (ISO/IEC 14882:2003): 5.2.11 Const cast [expr.const.cast]

더 알아보기