c++lambdac++17constexprscoping

Can a type inside a lambda parameter list refer to an uncaptured outer variable?


I'm trying to use std::size instead of macros like _countof or ARRAYSIZE, but I'm running into scoping problems.

Is the following code legal?

#include <iterator>

int main()
{
    int arr1[4];
    auto f = [](int(&arr2)[std::size(arr1)])
    {
        arr2[0] = 1;
    };
    (void)arr1;
    (void)f;
}

GCC and MSVC compile it fine, but Clang complains:

error: variable 'arr1' cannot be implicitly captured in a lambda with no capture-default specified
    auto f = [](int(&arr2)[std::size(arr1)])

Which one is correct?


Solution

  • I think Clang is correct.

    (Using terminology from the current C++23 draft.)

    std::size(arr1) is not part of an unevaluated operand, so the expression arr1 is a potentially-evaluated id-expression naming the variable arr1. The expression is also not part of a discarded-value expression, nor is the variable arr1 usable in constant expressions. Therefore the expression odr-uses arr1. (The variable could be made usable in constant expressions, making arr1 not an odr-use, by adding constexpr on it.)

    However, being a variable with automatic storage duration, arr1 is a local entity that is not odr-usable in the scope in which the expression appears. Specifically, to be odr-usable inside a lambda a local entity must be captured by the lambda, which arr1 is not. Even with a capture however, CWG 2380 clarifies that the local entity is odr-usable only in the block scope of the lambda, not in the function parameter declaration clause.

    A program that odr-uses a local enitity in a scope in which it isn't odr-usable is ill-formed.