C++ named requirements: Allocator
객체의 접근/주소 지정, 할당/해제 및 생성/소멸을 위한 전략을 캡슐화합니다.
스토리지 할당 또는 해제가 필요할 수 있는 모든 표준 라이브러리 구성 요소는 std::string , std::vector 및 모든 컨테이너 (단, std::array 제외) (C++11부터) 및 std::inplace_vector (C++26부터) 부터 std::shared_ptr 및 std::function (C++17 이전) 까지 다음 요구 사항을 충족하는 클래스 타입의 객체인 Allocator 를 통해 이를 수행합니다.
많은 할당자 요구 사항들의 구현은 선택적입니다. 왜냐하면 모든 AllocatorAwareContainer 가 할당자에 접근할 때 std::allocator_traits 를 통해 간접적으로 접근하며, std::allocator_traits 가 이러한 요구 사항들에 대한 기본 구현을 제공하기 때문입니다.
목차 |
요구사항
주어진
-
T, 비-const, 비-참조 타입 (C++11까지) 비-const 객체 타입 (C++11부터) (C++17까지) cv-한정되지 않은 객체 타입 (C++17부터) , -
A, Allocator 타입의T타입에 대한 할당자, -
a
,
A타입의 객체, -
B,A를 재바인딩하여 얻은 cv-한정되지 않은 객체 타입U에 대한 해당 Allocator 타입, -
b
,
B타입의 객체, - p , std:: allocator_traits < A > :: pointer 타입의 값으로 std:: allocator_traits < A > :: allocate ( ) 호출로 얻음,
- cp , std:: allocator_traits < A > :: const_pointer 타입의 값으로 p 로부터 변환으로 얻음,
- vp , std:: allocator_traits < A > :: void_pointer 타입의 값으로 p 로부터 변환으로 얻음,
- cvp , std:: allocator_traits < A > :: const_void_pointer 타입의 값으로 cp 또는 vp 로부터 변환으로 얻음,
-
xp
, cv-한정되지 않은 객체 타입
X에 대한 역참조 가능한 포인터, -
r
,
*
p
표현식으로 얻은
T타입의 lvalue, - n , std:: allocator_traits < A > :: size_type 타입의 값.
| 타입 식별자 | 별칭 타입 | 요구 사항 |
|---|---|---|
A::pointer
(선택적)
|
(지정되지 않음) [1] |
|
A::const_pointer
(선택적)
|
(지정되지 않음) |
|
A::void_pointer
(선택적)
|
(지정되지 않음) |
|
A::const_void_pointer
(선택적)
|
(지정되지 않음) |
|
A::value_type
|
T
|
|
A::size_type
(선택적)
|
(지정되지 않음) |
|
A::difference_type
(선택적)
|
(지정되지 않음) |
|
A::template rebind<U>::other
(선택적) [2] |
B
|
|
| 표현식 | 반환 타입 | 요구 사항 |
|---|---|---|
| * p |
T&
|
|
| * cp | const T & | * cp 와 * p 가 동일한 객체를 가리킴. |
| p - > m | (원본 유지) | ( * p ) . m 와 동일함, 단 ( * p ) . m 가 유효하게 정의된 경우. |
| cp - > m | (원본 유지) | ( * cp ) . m 와 동일함, 단 ( * cp ) . m 가 유효하게 정의된 경우. |
| static_cast < A :: pointer > ( vp ) | (원본 유지) | static_cast < A :: pointer > ( vp ) == p |
| static_cast < A :: const_pointer > ( cvp ) | (원본 유지) | static_cast < A :: const_pointer > ( cvp ) == cp |
| std:: pointer_traits < A :: pointer > :: pointer_to ( r ) | (원본 유지) |
| 표현식 | 반환 타입 | 요구 사항 |
|---|---|---|
| a. allocate ( n ) |
A::pointer
|
T[n]
타입의 배열 객체에 적합한 저장소를 할당하고 배열을 생성하지만, 배열 요소는 생성하지 않습니다. 예외를 던질 수 있습니다.
n
==
0
인 경우 반환 값은 명시되지 않습니다.
|
| a. allocate ( n, cvp ) (선택적) | a. allocate ( n ) 와 동일하지만, 지역성을 지원하기 위해 cvp ( nullptr 또는 a. allocate ( ) 로부터 얻은 포인터)를 명시되지 않은 방식으로 사용할 수 있습니다. | |
| a. allocate_at_least ( n ) (선택적) (C++23부터) |
std::
allocation_result
< A :: pointer > |
T[cnt]
타입의 배열 객체에 적합한 저장소를 할당하고 배열을 생성하지만 배열 요소는 생성하지 않으며,
{
p, cnt
}
를 반환합니다. 여기서
p
는 저장소를 가리키는 포인터이고
cnt
는
n
이상입니다. 예외를 던질 수 있습니다.
|
| a. deallocate ( p, n ) | (사용되지 않음) |
p
가 가리키는 저장소를 해제합니다.
p
는 이전
allocate
또는
allocate_at_least
(C++23부터)
호출에서 반환된 값이어야 하며, 그 사이에
deallocate
호출로 무효화되지 않아야 합니다.
n
은 이전에
allocate
에 전달된 값과 일치하거나
allocate_at_least
를 통해 요청된 요소 수와 반환된 요소 수 사이의 값이어야 합니다(어느 한계값과 같을 수 있음).
(C++23부터)
예외를 던지지 않습니다.
|
| a. max_size ( ) (선택적) |
A::size_type
|
A :: allocate ( ) 에 전달할 수 있는 가장 큰 값입니다. |
| a. construct ( xp, args... ) (선택적) | (사용되지 않음) |
이전에 할당된 저장소의
xp
가 가리키는 주소에
X
타입의 객체를 생성하며,
args...
를 생성자 인자로 사용합니다.
|
| a. destroy ( xp ) (선택적) | (사용되지 않음) |
xp
가 가리키는
X
타입의 객체를 파괴하지만, 저장소는 해제하지 않습니다.
|
| 표현식 | 반환 타입 | 요구사항 |
|---|---|---|
| a1 == a2 | bool |
|
| a1 ! = a2 |
|
|
| 선언 | 효과 | 요구사항 |
| A a1 ( a ) |
a1
==
a
가 되도록
a1
을 복사 생성함.
(참고: 모든 Allocator 는 CopyConstructible 를 만족함.) |
|
| A a1 = a | ||
| A a ( b ) |
B
(
a
)
==
b
이고
A
(
b
)
==
a
가 되도록
a
를 생성함.
(참고: 이는
rebind
로 관련된 모든 할당자들이 메모리 풀과 같은 서로의 자원을 유지함을 의미함.)
|
|
| A a1 ( std :: move ( a ) ) | a1 이 a 의 이전 값과 같도록 생성함. |
|
| A a1 = std :: move ( a ) | ||
| A a ( std :: move ( b ) ) | a 가 A ( b ) 의 이전 값과 같도록 생성함. |
|
| 타입 식별자 | 별칭 타입 | 요구사항 |
A::is_always_equal
(선택적) |
std::true_type 또는 std::false_type 또는 이들로부터 파생됨. |
|
| 표현식 | 반환 타입 | 설명 |
|---|---|---|
|
a.
select_on_container_copy_construction
(
)
(선택적) |
A
|
|
| 타입 식별자 | 별칭 타입 | 설명 |
A::propagate_on_container_copy_assignment
(선택적) |
std::true_type 또는 std::false_type 또는 이들로부터 파생된 타입. |
|
A::propagate_on_container_move_assignment
(선택적) |
|
|
A::propagate_on_container_swap
(선택적) |
|
참고 사항:
- ↑ 아래의 fancy pointers 도 참조하십시오.
-
↑
rebind는 이 할당자가SomeAllocator<T, Args>형태의 템플릿인 경우에만 선택사항입니다(이때Args는 0개 이상의 추가 템플릿 타입 매개변수이며, std::allocator_traits 에 의해 제공됨).
주어진
-
x1
와
x2
, (서로 다른 타입일 수 있는)
X::void_pointer,X::const_void_pointer,X::pointer, 또는X::const_pointer타입의 객체들
-
그러면,
x1
과
x2
가
동등한 값
을 가지는 포인터 값인 것은, 오직
x1
과
x2
모두가 이 네 가지 타입만을 사용하는 일련의
static_cast
를 통해
X::const_pointer타입의 두 개의 해당 객체 px1 과 px2 로 명시적으로 변환될 수 있고, 표현식 px1 == px2 가 true 로 평가되는 경우에 한합니다.
주어진
-
w1
와
w2
,
X::void_pointer타입의 객체들
-
그런 다음, 표현식
w1
==
w2
와
w1
!
=
w2
에 대해 하나 또는 두 객체 모두가
동등한 값
을 갖는
X::const_void_pointer타입의 객체로 대체될 수 있으며, 의미에는 변화가 없습니다.
주어진
-
p1
와
p2
,
X::pointer타입의 객체들
-
그런 다음, 표현식들
p1
==
p2
,
p1
!
=
p2
,
p1
<
p2
,
p1
<=
p2
,
p1
>=
p2
,
p1
>
p2
,
p1
-
p2
에 대해, 하나 또는 두 객체 모두를
동등한 값
을 가지는
X::const_pointer타입의 객체로 대체하더라도 의미에는 변화가 없습니다.
위 요구사항들은
Container
의
iterator
와
const_iterator
를 비교할 수 있게 합니다.
할당자 완전성 요구사항
타입
|
(C++17부터) |
상태 저장 및 상태 비저장 할당자
모든 Allocator 타입은 stateful 이거나 stateless 입니다. 일반적으로 stateful allocator 타입은 서로 다른 메모리 리소스를 나타내는 unequal 값을 가질 수 있는 반면, stateless allocator 타입은 단일 메모리 리소스를 나타냅니다.
|
사용자 정의 allocator가 stateless일 필요는 없지만, 표준 라이브러리에서 stateful allocator의 사용 여부와 방법은 구현에 따라 정의됩니다. 동일하지 않은 allocator 값을 사용하는 경우, 구현이 이러한 사용을 지원하지 않으면 구현에 따라 정의된 런타임 오류나 정의되지 않은 동작이 발생할 수 있습니다. |
(until C++11) |
|
사용자 정의 allocator는 상태를 포함할 수 있습니다. 각 컨테이너나 다른 allocator-aware 객체는 제공된 allocator의 인스턴스를 저장하며 std::allocator_traits 를 통해 allocator 교체를 제어합니다. |
(since C++11) |
상태 비저장 할당자 타입의 인스턴스는 항상 동일하게 비교됩니다. 상태 비저장 할당자 타입은 일반적으로 빈 클래스로 구현되며 empty base class optimization 에 적합합니다.
|
멤버 타입
|
(C++11부터) |
팬시 포인터
멤버 타입
pointer
가 raw pointer 타입이 아닌 경우, 일반적으로
"fancy pointer"
라고 부릅니다. 이러한 포인터들은 세그먼트 메모리 아키텍처를 지원하기 위해 도입되었으며, 현재는 raw pointer로 접근하는 동종 가상 주소 공간과 다른 주소 공간에 할당된 객체들에 접근하기 위해 사용됩니다. fancy pointer의 예시로는 매핑 주소 독립 포인터
boost::interprocess::offset_ptr
가 있으며, 이를 통해
std::set
과 같은 노드 기반 데이터 구조를 공유 메모리와 각 프로세스에서 다른 주소로 매핑된 메모리 매핑 파일에 할당할 수 있습니다. Fancy pointer는 이를 제공한 allocator와 독립적으로 사용할 수 있습니다
, 클래스 템플릿
std::pointer_traits
를 통해
(C++11부터)
.
함수
std::to_address
를 사용하여 fancy pointer에서 raw pointer를 얻을 수 있습니다.
(C++20부터)
|
표준 라이브러리에서 화려한 포인터(fancy pointers) 및 사용자 정의 크기/다른 타입 사용은 조건부로 지원됩니다. 구현에서는 멤버 타입
|
(C++11 이전) |
개념(Concept)쿼리 객체 std::get_allocator 의 정의를 위해 다음의 설명 전용(표현 전용) 개념이 정의됩니다.
설명 전용(표현 전용) 개념 /*simple-allocator*/ 는 Allocator 요구 사항의 최소 사용성 제약 조건을 정의합니다. |
(C++26부터) |
표준 라이브러리
다음 표준 라이브러리 구성 요소들은 Allocator 요구 사항을 충족합니다:
|
기본 할당자
(클래스 템플릿) |
|
|
(C++11)
|
다중 수준 컨테이너를 위한 다중 수준 할당자 구현
(클래스 템플릿) |
|
(C++17)
|
생성 시 사용된
std::pmr::memory_resource
기반의 런타임 다형성을 지원하는 할당자
(클래스 템플릿) |
예제
C++11 할당자를 보여주는 예제입니다.
[[
nodiscard
]]
는 C++20 스타일에 맞추기 위해 추가되었습니다.
#include <cstdlib> #include <iostream> #include <limits> #include <new> #include <vector> template<class T> struct Mallocator { typedef T value_type; Mallocator() = default; template<class U> constexpr Mallocator(const Mallocator <U>&) noexcept {} [[nodiscard]] T* allocate(std::size_t n) { if (n > std::numeric_limits<std::size_t>::max() / sizeof(T)) throw std::bad_array_new_length(); if (auto p = static_cast<T*>(std::malloc(n * sizeof(T)))) { report(p, n); return p; } throw std::bad_alloc(); } void deallocate(T* p, std::size_t n) noexcept { report(p, n, 0); std::free(p); } private: void report(T* p, std::size_t n, bool alloc = true) const { std::cout << (alloc ? "Alloc: " : "Dealloc: ") << sizeof(T) * n << " bytes at " << std::hex << std::showbase << reinterpret_cast<void*>(p) << std::dec << '\n'; } }; template<class T, class U> bool operator==(const Mallocator <T>&, const Mallocator <U>&) { return true; } template<class T, class U> bool operator!=(const Mallocator <T>&, const Mallocator <U>&) { return false; } int main() { std::vector<int, Mallocator<int>> v(8); v.push_back(42); }
가능한 출력:
Alloc: 32 bytes at 0x2020c20 Alloc: 64 bytes at 0x2023c60 Dealloc: 32 bytes at 0x2020c20 Dealloc: 64 bytes at 0x2023c60
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| LWG 179 | C++98 |
pointer
와
const_pointer
가 서로
비교 가능하도록 요구되지 않음 |
요구됨 |
| LWG 199 | C++98 |
a.allocate(0)
의 반환 값이
명확하지 않음 |
명시되지 않음 |
|
LWG 258
( N2436 ) |
C++98 |
할당자 간 동등 관계가 반사적, 대칭적,
또는 추이적일 것을 요구하지 않음 |
반사적, 대칭적,
추이적일 것을 요구 |
| LWG 274 | C++98 |
T
가 const 한정 타입이나 참조 타입일 수 있어
std::allocator
가 올바르지 않을 수 있음
[1]
|
이러한 타입들 금지 |
| LWG 2016 | C++11 |
할당자의 복사, 이동, 스왑 연산이
사용 시 예외를 던질 수 있음 |
예외를 던지지 않아야 함 |
| LWG 2081 |
C++98
C++11 |
할당자가 복사 할당(C++98)과 이동 할당(C++11)을
지원하도록 요구되지 않음 |
요구됨 |
| LWG 2108 | C++11 | 할당자가 상태를 가지지 않음을 나타낼 방법이 없음 |
is_always_equal
제공
|
| LWG 2263 | C++11 |
LWG 이슈 179
의 해결책이 C++11에서
실수로 누락되고
void_pointer
와
const_void_pointer
로 일반화되지 않음
|
복원 및 일반화 |
| LWG 2447 | C++11 |
T
가 volatile 한정 객체 타입일 수 있음
|
이러한 타입들 금지 |
| LWG 2593 | C++11 | 할당자로부터 이동 시 값이 변경될 수 있음 | 변경 금지 |
| P0593R6 | C++98 |
allocate
가 할당한 저장 공간에
배열 객체를 생성하도록 요구되지 않음 |
요구됨 |
-
↑
std::allocator
의 멤버 타입
reference와const_reference는 각각T&와const T&로 정의됩니다.