Я только что узнал, что (даже изменяемая) лямбда в C ++ не может быть назначена, если у нее нет пустых захватов (см. ClosureType :: operator =). Пример: auto x = 0; auto l0 = [copy = x] () изменяемый {}; auto l1 = [] () ...

4
m8mble 13 Дек 2020 в 15:41

1 ответ

Лучший ответ

Я подозреваю, что это не было предложено. Лямбды вошли в язык намного слабее, чем функциональные объекты, для которых они являются сахаром, и постепенно возвращают функциональность. Что касается специальных функций-членов, P0624 предложил добавить возможность присваивания и конструктивность по умолчанию для лямбда-выражений без захвата. В R0 была предложена только возможность построения по умолчанию, потому что это то, что нужно автору и, возможно, является наиболее очевидным недостатком, но возможность назначения была предложена в 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, потому что лямбда искусственно удаляет назначение, даже если его состояние захвата позволяет это. Перепишите это как тип функционального объекта, и unique_ptr станет присваиваемым, с сгенерированными операторами присваивания для типа функционального объекта.

Это всего лишь один пример, но, надеюсь, он проясняет, хотите ли вы назначить состояние захвата (std::ref(allocator)) - это не то же самое, что оператору вызова разрешено делать с состоянием захвата. (Ответ на связанный вопрос неверен.)

3
Jeff Garrett 14 Дек 2020 в 16:59