Лямбда-выражение с пустым захватом

Я столкнулся с интересным случаем (по крайней мере для меня) при использовании lambdas и задавался вопросом, является ли это ошибкой компилятора или чем-то, что разрешено стандартной функцией.

Разрежьте на погоню. Пример кода:

const int controlValue = 5;
std::vector<int> vect{ 0, 1, 2, 3 };
const auto result = std::any_of(vect.begin(), vect.end(), [](const int& item)
{
    return item == controlValue;
});

Обратите внимание, что переменная controlValue не записывается в выражение лямбда. Кроме того, в cppreference для лямбда-выражений указано, что [] - captures nothing

Использование VS2015 для компиляции приведенного выше кода дает ошибку, которая не удивительна:

error C3493: 'controlValue' cannot be implicitly captured because no default capture mode has been specified

Однако при использовании MinGW с gcc 4.8.2 один и тот же пример компилируется и работает. Некоторые онлайн-компиляторы, включая gcc 5.4.0, clang 3.8.0, дают аналогичный результат.

Когда controlValue теряет свой const, тогда все проверенные компиляторы дают ошибку, все ожидают (что переменная не захвачена, что хорошо).

Какой из компиляторов является стандартным в этом случае? Означает ли это, что для константных переменных здесь используются некоторые оптимизации или другие "хаки"? Может быть, что-то захватывается неявно? Может ли кто-нибудь объяснить ситуацию, происходящую здесь?

EDIT:

Некоторые отметили, что этот вопрос является дубликатом Lambda capture constexpr object. Хотя ответ может быть несколько связанным (указывает на случай использования odr), возникает вопрос об ошибке, возникающей при захвате по ref. Тема здесь совсем другая и фокусируется на том, чтобы не фиксировать явно переменную вообще (хотя использовать ее в теле лямбда).

Просматривая больше вопросов, связанных с лямбдой, если кто-то заинтересован, я бы указал на Использование lambda capture constexpr как измерение массива, который (как указано в @Barry) предлагает ошибку VS2015 и показывает, что установка переменной controlValue в примере здесь на static исправляет компиляцию под VS2015.

Ответы

Ответ 1

Это ошибка VS. Код отлично сформирован.

Правило в [expr.prim.lambda]:

Если лямбда-выражение или экземпляр шаблона оператора вызова функции общего лямбда odr использует (3.2) это или переменную с автоматическим временем хранения от ее области охвата, это лицо должно быть захваченным лямбда-выражением.

Если переменная используется odr, если, согласно [basic.def.odr]:

Переменная x, имя которой отображается как потенциально оцененное выражение ex, является odr, используемое ex , если применение преобразования lvalue-to-rvalue (4.1) к x не дает постоянного выражения (5.20) что не вызывает никаких нетривиальных функции, и, если x является объектом, ex является элементом множества потенциальных результатов выражения e, где либо преобразование lvalue-to-rval (4.1) применяется к e, либо e является выражением отбрасываемого значения (раздел 5).

И, из [expr.const]:

Условное выражение e является выражением постоянной константы, если оценка e, следуя правилам абстрактная машина (1.9), оценила бы одно из следующих выражений: [...] преобразование lvalue-to-rvalue (4.1), если оно не применяется к нелетучими значениям целого или перечисляемого типа, которые относятся к полному не- -устойчивый объект const с предшествующей инициализацией, инициализированный константным выражением

В:

return item == controlValue;

controlValue - это значение целочисленного типа, которое относится к полному нелетучивому объекту const, инициализированному константным выражением. Следовательно, когда мы используем controlValue в контексте, который включает преобразование lvalue-to-rvalue, он не используется odr. Поскольку он не используется не-odr, нам не нужно его захватывать.

Когда вы изменили controlValue на non const, оно перестает быть постоянным выражением, а проверка равенства odr - использует его. Так как он не захвачен, а используется odr, лямбда плохо сформирована.


Обратите внимание, что именно такой пример появляется в стандарте:

void f(int, const int (&)[2] = {}) { }   // #1
void f(const int&, const int (&)[1]) { } // #2
void test() {
    const int x = 17;
    auto g = [](auto a) {
        f(x); // OK: calls #1, does not capture x
    };

    // ...
}