C ++ 17에 std :: construct_at가없는 이유는 무엇입니까?
C ++ 17은를 추가 std::destroy_at
하지만 std::construct_at
대응하는 것은 없습니다 . 왜 그런 겁니까? 다음과 같이 간단하게 구현할 수 없습니까?
template <typename T, typename... Args>
T* construct_at(void* addr, Args&&... args) {
return new (addr) T(std::forward<Args>(args)...);
}
완전히 자연스럽지 않은 배치 새로운 구문 을 피할 수 있습니다.
auto ptr = construct_at<int>(buf, 1); // instead of 'auto ptr = new (buf) int(1);'
std::cout << *ptr;
std::destroy_at(ptr);
std::destroy_at
직접 소멸자 호출에 비해 두 가지 객관적인 개선 사항을 제공합니다.
중복성을 줄입니다.
T *ptr = new T; //Insert 1000 lines of code here. ptr->~T(); //What type was that again?
물론, 우리 모두는 그것을 a로 감싸서
unique_ptr
끝내는 것을 선호 하지만, 어떤 이유로 그렇게 할 수 없다면T
중복 요소가 있습니다. 유형을U
으로 변경하면 이제 소멸자 호출을 변경해야합니다. 그렇지 않으면 작업이 중단됩니다. 을 사용std::destroy_at(ptr)
하면 두 곳에서 같은 것을 변경할 필요가 없습니다.DRY가 좋습니다.
다음과 같이 쉽게 할 수 있습니다.
auto ptr = allocates_an_object(...); //Insert code here ptr->~???; //What type is that again?
포인터의 유형을 추론하면 삭제가 다소 어려워집니다. 당신은 할 수 없습니다
ptr->~decltype(ptr)()
; C ++ 파서는 그런 식으로 작동하지 않기 때문입니다. 뿐만 아니라decltype
유형을 포인터 로 추론 하므로 추론 된 유형에서 포인터 간접 지정을 제거해야합니다. 다음으로 안내 :auto ptr = allocates_an_object(...); //Insert code here using delete_type = std::remove_pointer_t<decltype(ptr)>; ptr->~delete_type();
누가 입력하고자하는 것을 ?
반대로 귀하의 가설 std::construct_at
은 배치보다 객관적인 개선을 제공하지 않습니다 new
. 두 경우 모두 생성중인 유형을 명시해야합니다. 생성자에 대한 매개 변수는 두 경우 모두 제공되어야합니다. 두 경우 모두 메모리에 대한 포인터를 제공해야합니다.
따라서 당신의 가설에 의해 해결 될 필요가 없습니다 std::construct_at
.
그리고 새로운 배치보다 객관적으로 능력 이 떨어 집니다. 다음과 같이 할 수 있습니다.
auto ptr1 = new(mem1) T;
auto ptr2 = new(mem2) T{};
이것들은 다릅니다 . 첫 번째 경우 개체는 기본적으로 초기화되어 초기화되지 않은 상태로 남을 수 있습니다. 두 번째 경우에는 개체가 값으로 초기화됩니다.
당신의 가설 std::construct_at
은 당신이 원하는 것을 고를 수 없습니다 . 매개 변수를 제공하지 않으면 기본 초기화를 수행하는 코드가있을 수 있지만 값 초기화를위한 버전을 제공 할 수 없습니다. 매개 변수없이 값을 초기화 할 수 있지만 기본적으로 개체를 초기화 할 수는 없습니다.
그런 일이 있지만 예상대로 이름이 지정되지 않았습니다 .
uninitialized_copy 는 객체 범위를 초기화되지 않은 메모리 영역으로 복사합니다.
uninitialized_copy_n (C ++ 11)은 여러 객체를 초기화되지 않은 메모리 영역 (함수 템플릿)에 복사합니다.
uninitialized_fill 은 범위 (함수 템플릿)에 의해 정의 된 초기화되지 않은 메모리 영역에 객체를 복사합니다.
- uninitialized_fill_n 은 객체를 시작과 카운트 (함수 템플릿)로 정의 된 초기화되지 않은 메모리 영역에 복사합니다.
- uninitialized_move (C ++ 17)는 객체 범위를 초기화되지 않은 메모리 영역 (함수 템플릿)으로 이동합니다.
- uninitialized_move_n (C ++ 17)은 많은 객체를 초기화되지 않은 메모리 영역 (함수 템플릿)으로 이동합니다.
- uninitialized_default_construct (C ++ 17)는 범위 (함수 템플릿)로 정의 된 초기화되지 않은 메모리 영역에서 기본 초기화에 의해 객체를 생성합니다.
- uninitialized_default_construct_n (C ++ 17)은 시작 및 개수 (함수 템플릿)로 정의 된 초기화되지 않은 메모리 영역에서 기본 초기화로 개체를 생성합니다.
- uninitialized_value_construct (C ++ 17)는 범위 (함수 템플릿)로 정의 된 초기화되지 않은 메모리 영역에서 값 초기화에 의해 개체를 생성합니다.
- uninitialized_value_construct_n (C ++ 17)은 시작 및 개수로 정의 된 초기화되지 않은 메모리 영역에서 값 초기화를 통해 개체를 생성합니다.
있습니다 std::allocator_traits::construct
. 이전에는에서 하나 더 std::allocator
있었지만 제거 된 이유는 표준위원회 문서 D0174R0에 있습니다.
std::construct_at
C ++ 20에 추가되었습니다. 그렇게 한 논문은 More constexpr container 입니다. 아마도 이것은 C ++ 17의 새로운 배치에 비해 충분한 이점이없는 것으로 보였지만 C ++ 20은 상황을 바꿉니다.
이 기능을 추가 한 제안의 목적은를 포함한 constexpr 메모리 할당을 지원하는 것 std::vector
입니다. 이를 위해서는 할당 된 저장소에 개체를 구성하는 기능이 필요합니다. 그러나의 관점에서 단순한 배치 새로운 거래가 void *
아닙니다 T *
. constexpr
평가는 현재 원시 저장소에 액세스 할 수있는 기능이 없으며위원회는이를 그대로 유지하려고합니다. 라이브러리 함수 std::construct_at
는 형식화 된 인터페이스를 추가합니다 constexpr T * construct_at(T *, Args && ...)
.
또한 사용자가 생성되는 형식을 지정할 필요가 없다는 장점이 있습니다. 포인터의 유형에서 추론됩니다. 새로운 배치를 올바르게 호출하는 구문은 다소 끔찍하고 직관적이지 않습니다. std::construct_at(ptr, args...)
와 비교하십시오 ::new(static_cast<void *>(ptr)) std::decay_t<decltype(*ptr)>(args...)
.
construct
구문상의 설탕을 제공하지 않는 것 같습니다. 또한 새로운 배치보다 효율성이 떨어집니다. 참조 인수에 바인딩하면 임시 구체화 및 추가 이동 / 복사 구성이 발생합니다.
struct heavy{
unsigned char[4096];
heavy(const heavy&);
};
heavy make_heavy(); // Return a pr-value
auto loc = ::operator new(sizeof(heavy));
// Equivalently: unsigned char loc[sizeof(heavy)];
auto p = construct<heavy>(loc,make_heavy()); // The pr-value returned by
// make_heavy is bound to the second argument,
// and then this arugment is copied in the body of construct.
auto p2 = new(loc) auto(make_heavy()); // Heavy is directly constructed at loc
//... and this is simpler to write!
불행히도 함수를 호출 할 때 이러한 추가 복사 / 이동 구성을 피할 수있는 방법이 없습니다. 전달은 거의 완벽합니다.
한편, construct_at
도서관에서는 표준 도서관 어휘를 완성 할 수 있습니다.
I think there should be a standard construct-function. In fact libc++ has one as an implementation detail in the file stl_construct.h
.
namespace std{
...
template<typename _T1, typename... _Args>
inline void
_Construct(_T1* __p, _Args&&... __args)
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
...
}
I think is it something useful to have because it allows to make "placement new" a friend. This is a great customization point for a move-only type that need uninitialized_copy
into the default heap (from an std::initializer_list
element for example.)
I have my own container library that reimplements a detail::uninitialized_copy
(of a range) to use a custom detail::construct
:
namespace detail{
template<typename T, typename... As>
inline void construct(T* p, As&&... as){
::new(static_cast<void*>(p)) T(std::forward<As>(as)...);
}
}
Which is declared a friend of a move-only class to allow copy only in the context of placement new.
template<class T>
class my_move_only_class{
my_move_only_class(my_move_only_class const&) = default;
friend template<class TT, class...As> friend void detail::construct(TT*, As&&...);
public:
my_move_only_class(my_move_only_class&&) = default;
...
};
ReferenceURL : https://stackoverflow.com/questions/52966560/why-isnt-there-a-stdconstruct-at-in-c17
'Nice programing' 카테고리의 다른 글
iOS 8 자동 셀 높이-마지막 행으로 스크롤 할 수 없음 (0) | 2020.12.25 |
---|---|
PHP에서 fopen 모드 "r +"와 "rw +"의 차이점은 무엇입니까? (0) | 2020.12.25 |
Tomcat 6을 삽입하는 방법? (0) | 2020.12.25 |
cmd.exe에서 "배치 작업 종료"를 억제하려면 어떻게해야합니까? (0) | 2020.12.25 |
PHP에서 개체를 캐스팅하는 방법 (0) | 2020.12.25 |