Ответ 1
Это сводится к тому, что лямбда-тип отличается от единиц перевода. Если это так, это может повлиять на вывод аргумента шаблона и потенциально вызвать разные функции, которые должны быть вызваны - в том, что должно быть последовательными определениями. Это нарушит ODR (см. Ниже).
Однако это не предназначено. На самом деле эта проблема уже давно затронута основной проблемой 765, в которой конкретно указаны встроенные функции с внешней связью - например, f
:
7.1.2 [dcl.fct.spec] параграф 4 указывает, что локальные статические переменные и строковые литералы, появляющиеся в теле встроенной функции с внешняя связь должна быть одной и той же сущностью в каждой единицы перевода в программе. Ничего не сказано, однако, о том, являются ли локальные типы также должны быть одинаковыми.
Хотя соответствующая программа всегда могла определить это с помощью typeid, недавние изменения в С++ (, позволяющие использовать локальные типы в качестве шаблона аргументы типа, классы закрытия выражения лямбда) делают этот вопрос более нажимаем.
Примечания к заседанию в июле 2009 года:
Типы должны быть одинаковыми.
Теперь разрешение включало следующую формулировку в [dcl.fct.spec]/4:
Тип, определенный внутри тела функции
extern inline
, является одним и тем же типом в каждой единицы перевода.
(NB: MSVC пока не относится к вышеуказанной формулировке, хотя он может быть в следующем выпуске).
Таким образом, Lambdas внутри таких тел функций безопасен, так как определение типа замыкания действительно находится в области блока ([expr.prim.lambda]/3 ). < ш > Следовательно, несколько определенийf
всегда были четко определены.
Эта резолюция, конечно же, не охватывает все сценарии, так как существует много других типов сущностей с внешней связью, которые могут использовать lambdas, в частности, шаблоны функций - это должно быть охвачено другой основной проблемой.
Тем временем Itanium уже содержит соответствующие правила, чтобы гарантировать, что такие типы лямбда совпадают в большем количестве ситуаций, поэтому Clang и GCC должны в основном вести себя так, как предполагалось.
Стандартная информация о том, почему различные типы замыканий являются нарушением ODR. Рассмотрим пулевые точки (6.2) и (6.4) в [basic.def.odr]/6:
Может быть более одного определения [...]. Для такого объекта с именем
D
, определенного в нескольких единицах перевода, каждое определениеD
должно состоять из та же последовательность жетонов; и(6.2) - в каждом определении
D
, соответствующих имен, просмотренных согласно [basic.lookup], относится к сущности, определенной в определениеD
или должно относиться к тому же объекту после ([over.match]) и после согласования частичного шаблонная специализация ([temp.over]), [...]; и(6.4) - в каждом определении
D
перегруженные операторы, о которых идет речь, неявные вызовы функций преобразования, конструкторы , новые функции оператора и функции удаления оператора, должны ссылаться на ту же функцию или функцию, определенную в определенииD
; [...]
Это эффективно означает, что любые функции, называемые в определении сущности, должны быть одинаковыми во всех единицах перевода - или были определены внутри его определения, как и локальные классы и их члены. То есть использование лямбда как таковое не является проблематичным, но ясно, что передача его в функциональные шаблоны есть, поскольку они определены вне определения.
В вашем примере с C
тип замыкания определяется внутри класса (область действия которого является наименьшей охватывающей). Если тип замыкания отличается в двух TU, которые стандарт может непреднамеренно подразумевать с уникальностью типа замыкания, конструктор создает экземпляр и вызывает различные специализации шаблона конструктора function
, нарушая (6.4) в приведенной выше цитате.