람다 할당 연산자를 삭제하는 이유는 무엇입니까?

Dec 13 2020

방금 mutableC ++ 의 (심지어 ) 람다 가 빈 캡처 가 없으면 할당 할 수 없다는 것을 배웠습니다 (cf ClosureType::operator=).

예:

auto x = 0;
auto l0 = [copy = x]() mutable {};
auto l1 = []() mutable {};

static_assert(not std::is_copy_assignable_v<decltype(l0)>);
static_assert(std::is_copy_assignable_v<decltype(std::ref(x))>);
static_assert(std::is_copy_assignable_v<decltype(l1)>);

이 선택의 근거를 묻고 있습니다. 왜 operator=삭제 되었습니까? 특히 시나리오에서, 즉 람다가 기본값 mutable이고 모든 캡처가 자체적으로 복사 할당 될 수있는 경우 (예 : l0위의 예 에서)?

나는 비 람다에 대한 이 관련 질문을 알고 mutable있습니다. 그러나 나는 그것을 해결하기보다는 결정을 이해하고 싶다.

답변

3 JeffGarrett Dec 14 2020 at 23:59

나는 그것이 제안되지 않았다고 생각합니다. Lambda는 그들이 설탕 인 함수 객체보다 훨씬 약한 언어로 들어갔고 천천히 기능을 되찾고 있습니다. 특수 멤버 함수와 관련하여 P0624 는 캡처없는 람다에 대한 할당 및 기본 구성을 추가 할 것을 제안했습니다. 저자가 필요로하고 가장 명백한 결함이기 때문에 R0에서는 default-constructibility 만 제안되었지만 R1에서는위원회 피드백을 기반으로 할당 가능성이 제안되었습니다.

캡처가있는 람다의 기본 구성 가능성은 확실히 언어와 일치합니다.

auto x1 = [i = 1]() { return i; };
static_assert(not std::is_default_constructible_v<decltype(x1)>); // why??

struct { int i = 1; auto operator()() { return i; } } x2;
static_assert(std::is_default_constructible_v<decltype(x2)>);

할당 가능성도 일관되고 유용합니다. 떠오르는 한 가지 예로서, std::default_delete할당 자 에 대한 아날로그를 갖는 제안이있었습니다. 즉, std::unique_ptr할당 자 할당 포인터 에 대한 템플릿 매개 변수로 사용할 수있는 유형입니다 . 람다를 사용하여 할당자를 캡처하고 이러한 목적으로 사용하는 것을 상상할 수 있습니다.

auto allocator_delete(auto& allocator) {
    using traits = typename std::allocator_traits<std::decay_t<decltype(allocator)>>;
    return [alloc=std::ref(allocator)](typename traits::pointer p) { traits::deallocate(alloc, p, 1); };
}
template<class Alloc> using allocator_deleter_t = decltype(allocator_delete(std::declval<Alloc&>()));
static_assert(not std::is_move_assignable_v<std::unique_ptr<int, allocator_deleter_t<std::allocator<int>>>>);
// why??

그러나 unique_ptr람다는 캡처 상태에서 허용하더라도 할당을 인위적으로 삭제하기 때문에 this를 리 바인드 (이동 할당) 할 수 없습니다. 이를 함수 객체 유형으로 다시 작성하고 함수 객체 유형에 unique_ptr대해 생성 된 할당 연산자를 사용하여 할당 가능합니다.

이는 하나의 예일 뿐이지 만 캡처 상태 ( std::ref(allocator)) 에 할당할지 여부 는 호출 연산자가 캡처 상태에 대해 수행 할 수있는 작업과 동일 하지 않음을 명확히합니다 . (연결된 질문의 답변이 잘못되었습니다.)