14

Here is a piece of code that won't compile in MSVC 2015 (ignore the uninitialized value access):

#include <array>
int main() {
    constexpr int x = 5;
    auto func = []() {
        std::array<int, x> arr;
        return arr[0];
    };
    func();
}

It complains that:

'x' cannot be implicitly captured because no default capture mode has been specified

But x is a constexpr! x is known at compile time to be 5. Why does MSVC kick up a fuss about this? (Is it yet another MSVC bug?) GCC will happily compile it.

Bernard
  • 5,209
  • 1
  • 34
  • 64

2 Answers2

12

The code is well-formed. The rule from [expr.prim.lambda] is:

If a lambda-expression or an instantiation of the function call operator template of a generic lambda odr-uses (3.2) this or a variable with automatic storage duration from its reaching scope, that entity shall be captured by the lambda-expression.

Any variable that is odr-used must be captured. Is x odr-used in the lambda-expression? No, it is not. The rule from [basic.def.odr] is:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.20) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression (Clause 5).

x is only used in a context where we apply the lvalue-to-rvalue conversion and end up with a constant expression, so it is not odr-used, so we do not need to capture it. The program is fine. This is the same idea as why this example from the standard is well-formed:

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
    };
    // ...
}
Barry
  • 286,269
  • 29
  • 621
  • 977
-2

Even though x is a constexpr, it is no different from any other object, otherwise, and follows the same rules with regards to scoping. There are no exceptions to scoping rules for constexprs, and a lambda must be coded to explicitly capture it.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Is there then a way to capture it but allow me to use it as a template parameter as in my code? – Bernard Mar 05 '17 at 15:55
  • That's another question. – Walter Mar 05 '17 at 15:59
  • clang would allow you to just capture it `[x]() {...}` and then use it as a template parameter, yet GCC doesn't. I'm confused. – DeiDei Mar 05 '17 at 16:00
  • 1
    @Bernard just make it `static`, no need to capture. – yuri kilochek Mar 05 '17 at 16:00
  • Capturing it by value, seems to be sufficient: https://software.intel.com/sites/default/files/m/d/4/1/d/8/icc.txt and there's nothing to be confused here. C++ is arguably one of the most complicated modern programming language, and it is not totally unexpected that some compiler occasionally fails to do things right in an obscure corner case. I haven't investigated if, indeed, capturing it by value is the kosher thing to do, and gcc chokes on it; but if so, opening a bug in gcc's bugzilla should get the ball rolling on fixing it. – Sam Varshavchik Mar 05 '17 at 16:02
  • @Sam On MSVC, it doesn't work whether you capture it by value or by reference. – Bernard Mar 05 '17 at 16:06
  • 6
    No, this isn't the case, -1. – Barry Mar 05 '17 at 17:27
  • @SamVarshavchik Looks like I should find out how to file bug reports for both MSVC *and* Clang (as mentioned by @cpplearner). Personally, I've never filed a bug report for anything before. – Bernard Mar 06 '17 at 16:20