为什么会出现 "use of local variable with automatic storage from containing function" 错误消息?

Why does the "use of local variable with automatic storage from containing function" error message exist?

我有一个 C++ language/compiler 设计好奇心问题。 我在 Cygwin 上使用 gcc 11.2.0,-std=gnu++17。

为什么在结构体中直接引用局部变量会报错? Lambda 函数一直这样做,它们基本上与包含 operator() 方法的结构没有什么不同。例如:

static inline void
   language_complaint( void )       // Grumble, grumble, grumble
{  printf("\nlanguage_complaint...\n");

   // Given that this compiles and runs...
   int foo= 22;
   printf("foo: %p->%d\n", &foo, foo);
   std::function bar= [=](void) { printf("foo: %p->%d\n", &foo, foo); };
   bar();

#if true // And so does this...
   struct named {
     int const foo;
     named(int foo) : foo(foo) {}   // Use constructor to copy local variable
     void operator()(void) { printf("foo: %p->%d\n", &foo, foo); }
   } one(foo);
   one();
   bar= one;

// ...And even this, which causes wild stores if improperly used... 
   struct name2 {
     int& foo;
     name2(int& foo) : foo(foo) {} // Use constructor to reference local variable
     void operator()(void) { *foo= 42; printf("foo: %p->%d\n", &foo, foo); }
   } two(foo);
   two();
   bar= two; 
   printf("foo: %p->%d\n", &foo, foo);

#else // Why, then, is this disallowed?
   struct {
     int /* const */ bar= foo;      // Error: Can't access local variable with auto storage
//   int const foo= ::foo;          // Error: Can't find ::foo. (Probably irrelevant)
     void operator()(void) { printf("bar: %p->%d\n", &bar, bar); }
   } one;
   one();
#endif
}

这是尝试将用例作为问题描述的一部分进行演示。相反,它混淆了问题。不过,我要离开它,以便下面的批评评论有意义。

我唯一能想到的理由是:复制自动变量以备后用时,无论用什么方法,容易踩到自己的脚趾。我知道,我的脚趾还在痛

但似乎 language/compiler 设计者正试图将您从自己身上拯救出来 但仅限于 普通结构,而不是 lambda 函数或构造函数初始化结构。示例:

编译是因为 int a 是静态的:

static int a= 'a'; // (Implicitly included in following examples)
static inline std::function<void(void)> ok1(void) {
  struct { 
    int b= a; 
    void operator()(void) {
      printf("a: %p->%d, b: %p->%d\n", &a, a, &b, b);
    } 
  } ret;
  return ret;
}

之所以编译,是因为 lambda 函数可以自由访问自动数据:

static inline std::function<void(void)> ok2(void) {
  int b= a; // Copy static "a" into automatic (stack) "b" 
  return [b](void) { (void)b; /* ignore b */ }; 
}

这个不能编译,因为它包含来自堆栈数据的赋值:

static inline std::function<void(void)> ng1(void) {
  int b= a;
  struct { int c= b; void operator()(void) { /* ignore c */ }} ret;
           ^^^^^^^^^ The error-generating statement
  return ret;
}

但是您可以使用一些额外的编码来访问自动变量:

static inline std::function<void(void)> ok3(void) {
  int b= a;
  struct X {
    int c; X(int p) : c(p) {} // Equivalent to c= b; 
    void operator()(void) { /* ignore c */ }
  } ret(b);
  return ret;
}

让我们测试一下:

static inline void test(void) {
  std::function<void(void)> f= ok1();
  f(); // a: 0x100402010->97, b: 0xffffcbb0->97

  a= 'b';
  f= ok1();
  f(); // a: 0x100402010->98, b: 0xffffcbb0->98

  a= 'c';
  f(); // a: 0x100402010->99, b: 0xffffcbb0->98
}

编译器专门测试并禁止自动变量赋值,原因不明。有吗? 用户、编译器或任何人或任何其他人从该限制中获得了什么?

所有具体工作的案例都涉及将表达式传递给构造函数。即使是 lambda 版本也需要您拼出要捕获的变量(或使用默认捕获)。这实际上是一种指定 lambda 构造函数的方法(这就是为什么你不能默认构造捕获 lambda 的原因)。

在 C++ 中,本地 class 基本上是方便的表示法。本地 class 声明及其中的定义的范围就好像它是在包含函数的定义之前定义的一样。因此,它无法访问任何函数局部名称,包括局部变量的名称。