const_cast
const_cast
const나 volatile 한정을 바꿔야 할 때가 있어요. 그런 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)
서로 다른 두 타입 T1과 T2에 대해, 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]