std:: launder
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Functions | ||||
|
(C++11)
|
||||
| Classes | ||||
|
(C++11)
|
||||
|
(C++17)
|
||||
| Types | ||||
| Objects | ||||
|
(C++20)
|
||||
| Object access | ||||
|
launder
(C++17)
|
|
헤더에 정의됨
<new>
|
||
|
template
<
class
T
>
constexpr T * launder ( T * p ) noexcept ; |
(C++17부터) | |
p 에 대한 디버추얼라이제이션 펜스. p 가 나타내는 객체와 동일한 주소를 가지면서, 객체가 원본 * p 객체와 가장 파생된 클래스가 다른 새로운 기본 클래스 서브오브젝트일 수 있는 객체에 대한 포인터를 반환합니다.
공식적으로, 주어진
-
포인터
p
가 메모리 내 바이트의 주소
A를 나타냄 -
객체
x
가 주소
A에 위치함 - x 가 자신의 수명 범위 내에 있음
-
x
의 타입이 모든 수준에서 cv-qualifier를 무시할 때
T와 동일함 - 결과를 통해 접근 가능한 모든 바이트가 p를 통해 접근 가능해야 함 (바이트는 객체 y 를 가리키는 포인터를 통해 접근 가능하며, 이러한 바이트들은 객체 z 와 포인터 상호 변환 가능 하거나 z 가 요소인 바로 바깥 배열 내에 위치하는 경우 해당 바이트에 접근 가능함).
그러면
std
::
launder
(
p
)
는 객체
x
를 가리키는
T*
타입의 값을 반환합니다. 그렇지 않으면 동작은 정의되지 않습니다.
프로그램은
T
가 함수 타입이거나 (cv 한정자가 있을 수 있는)
void
인 경우 형식이 잘못되었습니다.
std::launder
는 인수의 (변환된) 값이 함수 호출을 대체하여 사용될 수 있는 경우에만
핵심 상수 표현식
에서 사용될 수 있습니다. 다시 말해,
std::launder
는 상수 평가에서의 제한을 완화하지 않습니다.
참고 사항
std::launder
는 인자에 아무런 영향을 미치지 않습니다. 해당 객체에 접근하기 위해서는 반환값을 사용해야 합니다. 따라서 반환값을 버리는 것은 항상 오류입니다.
std::launder
의 일반적인 사용 사례는 다음과 같습니다:
- 동일한 타입의 기존 객체 저장소에 생성된 객체에 대한 포인터를 획득하는 경우, 이때 이전 객체에 대한 포인터는 재사용될 수 없음 (예: 두 객체 중 하나가 기본 클래스 하위 객체인 경우);
-
객체에 저장소를 제공하는 객체의 포인터로부터 placement
new를 통해 생성된 객체에 대한 포인터를 획득하는 경우.
도달 가능성
제한은
std::launder
가 원본 포인터를 통해 접근할 수 없는 바이트에 접근하는 데 사용될 수 없도록 보장하여, 컴파일러의 탈출 분석을 방해하지 않게 합니다.
int x[10]; auto p = std::launder(reinterpret_cast<int(*)[10]>(&x[0])); // 정상 int x2[2][10]; auto p2 = std::launder(reinterpret_cast<int(*)[10]>(&x2[0][0])); // 정의되지 않은 동작: x2[1]이 결과 포인터를 통해 x2[0]에 도달 가능하지만 // 소스에서는 도달할 수 없음 struct X { int a[10]; } x3, x4[2]; // 표준 레이아웃; 패딩이 없다고 가정 auto p3 = std::launder(reinterpret_cast<int(*)[10]>(&x3.a[0])); // 정상 auto p4 = std::launder(reinterpret_cast<int(*)[10]>(&x4[0].a[0])); // 정의되지 않은 동작: x4[1]이 결과 포인터를 통해 x4[0].a에 도달 가능하지만 // (이는 x4[0]과 포인터 상호 변환 가능) 소스에서는 도달할 수 없음 struct Y { int a[10]; double y; } x5; auto p5 = std::launder(reinterpret_cast<int(*)[10]>(&x5.a[0])); // 정의되지 않은 동작: x5.y가 결과 포인터를 통해 x5.a에 도달 가능하지만 // 소스에서는 도달할 수 없음
예제
#include <cassert> #include <cstddef> #include <new> struct Base { virtual int transmogrify(); }; struct Derived : Base { int transmogrify() override { new(this) Base; return 2; } }; int Base::transmogrify() { new(this) Derived; return 1; } static_assert(sizeof(Derived) == sizeof(Base)); int main() { // 사례 1: 새로운 객체가 기본 하위 객체이지만 이전 객체가 완전한 객체이기 때문에 // 투명하게 대체 가능하지 않음 Base base; int n = base.transmogrify(); // int m = base.transmogrify(); // 정의되지 않은 동작 int m = std::launder(&base)->transmogrify(); // OK assert(m + n == 3); // 사례 2: 바이트 배열을 통해 제공된 저장 공간을 가진 새 객체에 // 배열에 대한 포인터를 통해 접근 struct Y { int z; }; alignas(Y) std::byte s[sizeof(Y)]; Y* q = new(&s) Y{2}; const int f = reinterpret_cast<Y*>(&s)->z; // 클래스 멤버 접근은 정의되지 않은 동작: // reinterpret_cast<Y*>(&s)는 // "s를 가리키는 포인터" 값을 가지며 // Y 객체를 가리키지 않음 const int g = q->z; // OK const int h = std::launder(reinterpret_cast<Y*>(&s))->z; // OK [](...){}(f, g, h); // [[maybe_unused]] 효과 유발 }
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| LWG 2859 | C++17 |
도달 가능(reachable)
의 정의가 포인터-상호 변환 가능 객체로부터의
포인터 연산을 고려하지 않음 |
포함됨 |
| LWG 3495 | C++17 |
std::launder
가 상수 표현식에서 비활성 멤버에 대한 포인터를
역참조 가능하게 만들 수 있음 |
금지됨 |