static_cast
conversion
암시적 변환과 사용자 정의 변환의 조합을 사용하여 타입 간 변환을 수행합니다.
목차 |
구문
static_cast<
target-type
>(
expression
)
|
|||||||||
target-type 타입의 값을 반환합니다.
설명
다음 변환들만 static_cast 를 사용하여 수행할 수 있으며, 이러한 변환이 constness 제거 (또는 volatility 제거)를 유발하는 경우는 예외입니다.
Base
" 타입의 lvalue이고
target-type
이 "참조
cv2
Derived
"인 경우, 다음 조건들이 모두 만족되면 결과는
expression
을 포함하는
Derived
타입 객체를 참조합니다:
-
Derived는 완전한 클래스 타입입니다. -
Base는Derived의 기본 클래스입니다. - cv1 이 cv2 보다 더 큰 cv-한정자를 가지지 않습니다.
Derived
타입 객체의 기본 클래스 하위 객체가 아닌 경우, 동작은 정의되지 않습니다.
struct B {}; struct D : B { B b; }; D d; B& br1 = d; B& br2 = d.b; static_cast<D&>(br1); // OK, lvalue denoting the original “d” object static_cast<D&>(br2); // UB: the “b” subobject is not a base class subobject
|
2)
target-type
이 "
Derived
에 대한 rvalue 참조"이고
expression
이 "(possibly cv-qualified)
Base
" 타입의 xvalue이며
Base
가
Derived
의 기본 클래스인 경우, 이러한 변환의 결과와 제약 조건은 "
Base
lvalue에서
Derived
참조" 변환과 동일합니다.
3)
target-type
이 rvalue 참조 타입이고 참조된 타입이
expression
의 타입과
reference-compatible
한 경우,
static_cast
는
glvalue, class prvalue, 또는 array prvalue
(until C++17)
모든 lvalue
(since C++17)
expression
의 값을 해당 표현식과 동일한 객체를 참조하는 xvalue로, 또는 해당 기본 클래스 하위 객체로 변환합니다(
target-type
에 따라 다름).
[1]
target-type
이
expression
의 타입에 대한 접근 불가능하거나 모호한 기본 클래스인 경우, 프로그램은 ill-formed입니다.
|
(since C++11) |
|
선언문 target-type temp ( expression ) ; 이 어떤 임시 변수 temp 에 대해 올바른 형식일 때. 이러한 명시적 변환의 효과는 선언 및 초기화를 수행한 후 temp 를 변환 결과로 사용하는 것과 동일합니다. expression 은 초기화가 이를 lvalue (C++11까지) glvalue (C++11부터) 로 사용하는 경우에만 lvalue (C++11까지) glvalue (C++11부터) 로 사용됩니다. |
(C++17까지) | ||
|
다음 조건 중 하나가 충족될 때:
명시적 변환은 다음과 같이 정의됩니다:
|
(C++17부터) |
- lvalue-to-rvalue conversion
- array-to-pointer conversion
- function-to-pointer conversion
- null pointer conversion
- null member pointer conversion
- boolean conversion
| (C++17부터) |
|
a)
범위 있는 열거형
타입의 값은 정수 또는 부동소수점 타입으로 변환될 수 있습니다.
|
(C++11 이후) |
- 만약 target-type 이 고정된 기반 타입을 가지고 있다면, expression 은 먼저 integral promotion 또는 integral conversion 을 통해 해당 타입으로 변환된 후, 필요하다면 target-type 으로 변환됩니다.
- 만약 target-type 이 고정된 기반 타입을 가지고 있지 않다면, 원래 값이 열거형 값의 범위 내에 있는 경우 expression 의 값은 변경되지 않으며, 그렇지 않은 경우 동작은 정의되지 않습니다.
|
d)
부동소수점 타입의 prvalue는 명시적으로 다른 부동소수점 타입으로 변환될 수 있습니다.
|
(C++23부터) |
Base
"는 다음 조건들이 모두 충족될 경우 타입 "pointer to
cv2
Derived
"로 명시적으로 변환될 수 있습니다:
-
Derived는 완전한 클래스 타입입니다. -
Base는Derived의 기본 클래스입니다. - cv1 이 cv2 보다 더 큰 cv-qualification이 아닙니다.
Base
타입 객체를 포함하는
Derived
타입 객체에 대한 포인터입니다.
-
Base가Derived의 virtual base class 인 경우. -
Base가Derived의 virtual base class의 base class인 경우. -
"pointer to
Derived"에서 "pointer toBase"로의 유효한 표준 변환이 존재하지 않는 경우.
Derived
타입 객체의 base class 서브오브젝트를 가리키지 않는 경우, 동작은 정의되지 않습니다.
T
타입의
Derived
멤버에 대한 포인터"는 다음 모든 조건이 충족될 때 타입 "cv2
T
타입의
Base
멤버에 대한 포인터"로 명시적으로 변환될 수 있습니다:
-
Derived는 완전한 클래스 타입입니다. -
Base는Derived의 기본 클래스입니다. - cv1 이 cv2 보다 더 큰 cv-한정자가 아닙니다.
Base
클래스의 원본 (간접적일 수 있는) 멤버에 대한 포인터입니다.
T
의
Base
멤버에 대한 포인터"에서 "타입
T
의
Derived
멤버에 대한 포인터"로의 유효한 표준 변환이 존재하지 않으면 프로그램은 형식이 잘못되었습니다.
Base
클래스의 (직접적이거나 간접적인) 멤버가 아닌 경우, 동작은 정의되지 않습니다.
T
가 객체 타입인 경우, 타입 "pointer to
cv2
T
"로 명시적으로 변환될 수 있습니다.
|
(C++17 이전) |
|
(C++17 이후) |
모든 형변환 표현식과 마찬가지로, 결과는 다음과 같습니다:
- target-type 이 lvalue 참조 타입인 경우 lvalue 또는 함수 타입에 대한 rvalue 참조인 경우 (C++11부터) ;
|
(since C++11) |
- 그렇지 않으면 prvalue입니다.
- ↑ 이 유형의 static_cast 는 std::move 에서 이동 의미론을 구현하는 데 사용됩니다.
- ↑ IEEE 연산이 지원되는 경우, 반올림은 기본적으로 가장 가까운 값으로 설정됩니다.
포인터 상호 변환 가능 객체
두 객체 a 와 b 가 다음 조건을 만족할 때 포인터 상호 변환 가능(pointer-interconvertible) 합니다:
- 동일한 객체이거나,
- 하나는 공용체 객체이고 다른 하나는 해당 객체의 비정적 데이터 멤버이거나,
- 하나는 standard-layout 클래스 객체이고 다른 하나는 해당 객체의 첫 번째 비정적 데이터 멤버 또는 해당 객체의 기본 클래스 하위 객체이거나,
- 객체 c 가 존재하여 a 와 c 가 포인터 상호 변환 가능하고, c 와 b 가 포인터 상호 변환 가능한 경우.
union U { int a; double b; } u; void* x = &u; // x의 값은 "u를 가리키는 포인터"입니다 double* y = static_cast<double*>(x); // y의 값은 "u.b를 가리키는 포인터"입니다 char* z = static_cast<char*>(x); // z의 값은 "u를 가리키는 포인터"입니다
참고 사항
기본-파생 변환(
다운캐스트
)을
static_cast
로 수행할 경우, 포인터/참조 대상 객체의
동적 타입
이
Derived
인지 확인하기 위한 런타임 검사를 수행하지 않습니다. 따라서
정적 다형성
구현과 같이 다른 방법으로 이 전제 조건이 보장된 경우에만 안전하게 사용할 수 있습니다. 안전한 다운캐스트는
dynamic_cast
를 사용하여 수행할 수 있습니다.
static_cast 는 특정 타입에 대한 함수-포인터 변환을 수행하여 함수 오버로드를 명확히 하는 데에도 사용될 수 있습니다. 예를 들어 다음과 같습니다:
std::for_each(files.begin(), files.end(), static_cast<std::ostream&(*)(std::ostream&)>(std::flush));
키워드
예제
#include <iostream> #include <vector> struct B { int m = 42; const char* hello() const { return "Hello world, this is B!\n"; } }; struct D : B { const char* hello() const { return "Hello world, this is D!\n"; } }; enum class E { ONE = 1, TWO, THREE }; enum EU { ONE = 1, TWO, THREE }; int main() { // 1. 정적 다운캐스트 D d; B& br = d; // 암시적 변환을 통한 업캐스트 std::cout << "1) " << br.hello(); D& another_d = static_cast<D&>(br); // 다운캐스트 std::cout << "1) " << another_d.hello(); // 3. 좌측값에서 소멸값으로 std::vector<int> v0{1, 2, 3}; std::vector<int> v2 = static_cast<std::vector<int>&&>(v0); std::cout << "3) 이동 후, v0.size() = " << v0.size() << '\n'; // 4. 폐기값 표현식 static_cast<void>(v2.size()); // 5. 초기화 변환 int n = static_cast<int>(3.14); std::cout << "5) n = " << n << '\n'; std::vector<int> v = static_cast<std::vector<int>>(10); std::cout << "5) v.size() = " << v.size() << '\n'; // 6. 암시적 변환의 역변환 void* nv = &n; int* ni = static_cast<int*>(nv); std::cout << "6) *ni = " << *ni << '\n'; // 7a. 범위 있는 열거형을 int로 E e = E::TWO; int two = static_cast<int>(e); std::cout << "7a) " << two << '\n'; // 7b. int를 열거형으로, 열거형을 다른 열거형으로 E e2 = static_cast<E>(two); [[maybe_unused]] EU eu = static_cast<EU>(e2); // 7f. 멤버 포인터 업캐스트 int D::*pm = &D::m; std::cout << "7f) " << br.*static_cast<int B::*>(pm) << '\n'; // 7g. void*를 임의 객체 포인터로 void* voidp = &e; [[maybe_unused]] std::vector<int>* p = static_cast<std::vector<int>*>(voidp); }
출력:
1) Hello world, this is B! 1) Hello world, this is D! 3) after move, v0.size() = 0 5) n = 3 5) v.size() = 10 6) *ni = 3 7a) 2 7f) 42
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 137 | C++98 |
void
포인터의 const성과 volatile성을
제거할 수 있었음 |
이러한 경우 cv-한정자를
제거할 수 없음 |
| CWG 427 | C++98 | 다운캐스트가 직접 초기화와 모호할 수 있었음 | 이 경우 다운캐스트를 선택함 |
| CWG 439 | C++98 |
"객체 포인터"를 "
void
포인터"로 변환한 후
다시 원래 포인터로 변환할 때, 결과 타입이 동일한 cv-한정자를 가져야만 값이 보존됨 |
cv-한정자가
달라도 됨 |
| CWG 1094 | C++98 |
부동소수점 값에서 열거형 값으로의
변환이 명시되지 않았음 |
명시됨 |
| CWG 1320 | C++11 |
범위 있는 열거형 값에서
bool로의 변환이 명시되지 않았음 |
명시됨 |
| CWG 1412 | C++98 |
"
void
포인터"에서 "객체 포인터"로의
변환 결과가 불명확했음 |
명확해짐 |
| CWG 1447 | C++11 |
비트 필드에서 rvalue 참조로의 변환이
명시되지 않았음 (비트 필드에 참조를 바인딩할 수 없음) |
명시됨 |
| CWG 1766 | C++98 |
정수 또는 열거형 값에서 열거형 값으로의 변환 시
expression 이 범위를 벗어나면 결과가 명시되지 않았음 |
이 경우 동작이
정의되지 않음 |
| CWG 1832 | C++98 |
정수 또는 열거형 값에서 열거형 값으로의
변환이 target-type 이 불완전할 수 있었음 |
허용되지 않음 |
| CWG 2224 | C++98 |
기본 클래스 타입의 멤버에서 파생 클래스 타입의
완전한 객체로의 변환이 유효했음 |
이 경우 동작이
정의되지 않음 |
| CWG 2254 | C++11 |
데이터 멤버가 없는 표준 레이아웃 클래스 객체가
첫 번째 기본 클래스와 포인터 상호 변환 가능했음 |
모든 기본 클래스와
포인터 상호 변환 가능함 |
| CWG 2284 | C++11 |
비표준 레이아웃 공용체 객체와 해당 객체의
비정적 데이터 멤버가 포인터 상호 변환 가능하지 않았음 |
포인터 상호 변환 가능함 |
| CWG 2310 | C++98 |
기본-파생 포인터 변환과
파생-기본 포인터-멤버 변환에서 파생 클래스 타입이 불완전할 수 있었음 |
완전해야 함 |
| CWG 2338 | C++11 |
고정된 기본 타입을 가진 열거형 타입으로의 변환 시
expression 이 범위를 벗어나면 정의되지 않은 동작이 발생함 |
먼저 기본 타입으로
변환함 (정의되지 않은 동작 없음) |
| CWG 2499 | C++11 |
표준 레이아웃 클래스가 모든 기본 하위 객체가 동일한 주소를 가지더라도
포인터 상호 변환 불가능한 기본 클래스를 가질 수 있었음 |
가지지 않음 |
| CWG 2718 | C++98 |
기본-파생 참조 변환에서
파생 클래스 타입이 불완전할 수 있었음 |
완전해야 함 |
| CWG 2882 | C++98 |
static_cast
<
void
>
(
expr
)
가
expr 에서 void 로의 암시적 변환 시퀀스를 형성하려는 시도를 하는지 불명확했음 |
이 경우 시도하지 않음 |
참조문헌
- C++23 표준 (ISO/IEC 14882:2024):
-
- 7.6.1.9 정적 변환 [expr.static.cast]
- C++20 표준(ISO/IEC 14882:2020):
-
- 7.6.1.8 Static cast [expr.static.cast]
- C++17 표준 (ISO/IEC 14882:2017):
-
- 8.2.9 Static cast [expr.static.cast]
- C++14 표준(ISO/IEC 14882:2014):
-
- 5.2.9 정적 변환 [expr.static.cast]
- C++11 표준 (ISO/IEC 14882:2011):
-
- 5.2.9 정적 변환 [expr.static.cast]
- C++98 표준(ISO/IEC 14882:1998):
-
- 5.2.9 Static cast [expr.static.cast]
- C++03 표준(ISO/IEC 14882:2003):
-
- 5.2.9 정적 변환 [expr.static.cast]