LeoPro's blog

By LeoPro, history, 15 months ago, In Russian

Hello!

I have yet another C++ question I want to know answer to. That is:

  • This code doesn't compile with error message use of deleted function ‘main()::<lambda(auto:1)>::~<lambda>()’
int main() { // CE
    int n = 1;
    int a[n];
    auto f = [&](auto f) -> void {
        a[0] = 0;
    };
}
  • On the other side, replacing C-array with vector or specifying array length as compile-time constant or allocating it with new — every other way makes everything to work:
int main() { // OK
    int n = 1;
    vector<int> x(n);
    int y[1];
    int *z = new int[n];
    auto f = [&](auto f) -> void {
        x[0] = y[0] = z[0] = 0;
    };
}
  • The error also goes off if the lambda f doesn't accept any arguments declared as auto. It also disappears if I capture the array by link directly: auto f = [&, &a].

So, why is it the case? I managed to find out that variable sized array are GCC extension but it doesn't clarify the doubt. The problem is not in the array allocation / initialization, but in lambda's destructor, which clearly needn't to destruct the array.

As far as I know, the array is just a pointer to its beginning and capturing it by copy, or by reference makes small difference — you either create new variable that points to the beginning of the array too, or you create a reference to the "main" pointer. Doesn't seem to be a problem.

So, this is quite a peculiar problem; it arose when I tried to combine recursive lambdas and C-arrays. Probably some C++ master would share some thoughts and improve my understanding and intuition of the language.

  • Vote: I like it
  • +10
  • Vote: I do not like it

»
15 months ago, # |
  Vote: I like it +16 Vote: I do not like it

Intuition is easy: type of lambda f is smth like:

struct UniqueClass {
    int& n;
    int a[n]; //reference to it, hell knows how it is written in gcc
    template<typename T>
    void operator()(T f) {
        a[0] = 0;
    }
};

And question there is "What type of a"? And that must be known type with known compilation time size (C++ required each object to be known size at the moment of creation).

Answer is "since variable length array is not C++ standartized so it is whatever GCC decides to be".

Example 1:

auto f = [&](int f) -> void {  
    a[0] = 0;  
};  

Sucessfully compiles in GCC 7.3 C++17 (which is confusing since in labmdas without VLA auto param would be correct starts from C++14)

Example 2:

int main() {
    int n = 4;
    int a[n];
    auto f = [&](int f) -> void {
        std::cout << sizeof(a) << std::endl;
    };
    f(0);
}

Compiles and write 4 in GCC 7.3 C++17

Example #3:

int main() {
    int n = 4;
    int a[n];
    auto f = [&](int f) -> void {
        std::cout << sizeof(a) << std::endl;
    };
    f(0);
}

Compiles and write 16 in GCC 10.2 C++20

Bottom line: don't use feature if you don't understand how it works. And don't use VLA in particular outside trivial cases (because it is designed for trivial cases like "Allocate local array without any move/copy of it").