Почему лямбда в С++ никогда не является DefaultConstructible

У меня есть лямбда, которые ничего не захватывают, например

[](){};

У меня есть класс шаблонов, содержащий такой лямбда. Поскольку лямбда не содержит нестатических членов данных, а также виртуальных функций, это должен быть пустой класс и DefaultConstructible. Это всего лишь класс политики, пригодный для метапрограммирования шаблонов. Я хотел бы знать, почему такой класс не является стандартным по умолчанию стандартом С++.

Sidenote: Понимание того, как тип закрытия Lambda удалил конструктор по умолчанию, задает другой вопрос, хотя название похоже очень похоже. Он спрашивает, как создается объект lambda-объекта без сохранения без использования стандартного конструктора по умолчанию. Я спрашиваю, почему нет подходящего конструктора по умолчанию.

Ответ 1

Lambdas предназначены для создания, а затем используются. Стандарт, таким образом, говорит "нет, у них нет конструктора по умолчанию". Единственный способ сделать это - через лямбда-выражение или его копии.

Они не предназначены для того, чтобы их типы были чем-то, что вы держите и используете. В связи с этим риски нарушения ODR и требование, чтобы компиляторы избегали нарушений ODR, сделало бы чередование символов чрезмерно сложным.

Тем не менее, в С++ 17 вы можете написать обертку без атрибута вокруг указателя функции:

template<auto fptr>
struct function_pointer_t {
  template<class...Args>
  // or decltype(auto):
  std::result_of_t< std::decay_t<decltype(fptr)>(Args...) >
  operator()(Args&&...args)const
    return fptr(std::forward<Args>(args)...);
  }
};

И поскольку operator void(*)() on [](){} constexpr в С++ 17, function_pointer_t<+[](){}> - функция do-nothing объект, который является DefaultConstructible.

Это фактически не переносит лямбда, а скорее указатель на функцию, создаваемую лямбдой.

Ответ 2

Я предполагаю, что вы знакомы с различием между типами, объектами и выражениями. В С++ лямбда конкретно относится к лямбда-выражению. Это удобный способ обозначить нетривиальный объект. Однако это удобство: вы можете создать похожий объект самостоятельно, выписав код.

Теперь для правил С++ каждое выражение имеет тип, но этот тип не предназначен для лямбда-выражений. Вот почему это неназванный и уникальный тип - комитет С++ не счел целесообразным определять эти свойства. Аналогично, если было определено, что он имеет значение по умолчанию, стандарт должен определять поведение. В текущем правиле нет необходимости определять поведение по умолчанию ctor.

Как вы заметили, для частного случая [](){} тривиально определить значение по умолчанию ctor. Но в этом нет никакого смысла. Вы сразу переходите к первому трудному вопросу: для какой лямбда должен быть задан по умолчанию ctor? Какое подмножество лямбды достаточно просто, чтобы иметь достойное определение, но достаточно сложное, чтобы быть интересным? Без консенсуса вы не можете ожидать, что это будет стандартизировано.

Обратите внимание, что поставщики компилятора, как расширение, уже могли это предложить. Стандартизация часто следует существующей практике, см. Boost. Но если ни один поставщик компилятора не считает это целесообразным, почему они так думают в унисон?