【问题标题】:Why field inside a local class cannot be static?为什么本地类中的字段不能是静态的?
【发布时间】:2011-05-26 10:48:53
【问题描述】:
void foo (int x)
{
  struct A { static const int d = 0; }; // error
}

除了来自标准的参考之外,这背后是否有任何动机来禁止内部类中的 static 字段?

error: field `foo(int)::A::d' in local class cannot be static

编辑:但是,static 成员函数是允许的。对于这种情况,我有一个用例。假设我希望只为 POD 调用 foo(),那么我可以像这样实现它,

template<typename T>
void foo (T x)
{
  struct A { static const T d = 0; }; // many compilers allow double, float etc.
}

foo() 应该只传递给 POD(如果允许 static)而不传递给其他数据类型。这只是我想到的一个用例。

【问题讨论】:

标签: c++ static-members local-class


【解决方案1】:

我猜是因为静态类成员必须在全局范围内定义。

编辑:

抱歉,我是个懒鬼,只是扔东西 :) 更准确一点。类的静态成员需要在全局范围内定义,例如

foo.h

class A {
  static int dude;
};

foo.cpp

int A::dude = 314;

现在,由于 void foo(int x) 内部的范围不是全局的,因此没有定义静态成员的范围。希望这更清楚一点。

【讨论】:

  • 当您可以在函数内简单地声明static 变量时,它怎么能成为全局范围? [注意:我没有投反对票,但在它失控之前,我建议编辑你的答案。]
  • @iammilind:这正是问题所在。如果允许,则该成员必须是,但不能在全局范围内定义。由于 定义 是不可能的,标准使 声明 非法。这意味着您已经在编译时得到错误,而不是链接时。
  • 感谢您支持 Salters :D
  • @MSalters,我现在意识到@Magnus 的答案更准确。但由于缺乏描述,有人否决了它。 @Magnus 您能否详细说明一下以供其他人理解。我会接受答案。谢谢。
  • @iammilind:我稍微修改了我的答案。如果您想让我更准确一些,请告诉我。
【解决方案2】:

Magnus Skog 给出了真正的答案:静态数据成员只是一个声明;该对象必须在其他地方定义,在命名空间范围内,并且类定义在命名空间范围内不可见。

请注意,此限制仅适用于静态数据成员。这意味着有一个简单的解决方法:

class Local
{
    static int& static_i()
    {
        static int value;
        return value;
    }
};

这为您提供完全相同的功能,但代价是 使用函数语法来访问它。

【讨论】:

  • 这完全一样吗? IIRC 静态类成员的地址是有效的非类型模板参数,这显然不是。
  • +1,您的回答非常具有解释性,也感谢您的工作。我希望我能接受 2 个答案。 :)
  • @MSalters 好点,至少部分如此。带有初始值设定项的静态整型 const 将是整型常量表达式,可用作非类型模板参数或 C 样式数组的维度。对于其他静态成员,我认为您会满足对象具有外部链接的要求,而本地类或其成员则不是这种情况。
【解决方案3】:

因为没有人认为有必要?

[edit]:静态变量只需要定义一次,通常在类之外(内置函数除外)。允许它们在本地类中也需要设计一种定义它们的方法。 [/edit]

添加到语言中的任何功能都是有代价的:

  • 必须由编译器实现
  • 它必须在编译器中维护(并且可能会引入错误,即使在其他功能中也是如此)
  • 它存在于编译器中(因此即使在未使用时也可能会导致速度变慢)

有时,不实施一项功能是正确的决定。

本地函数和类已经给语言增加了难度,但收效甚微:使用static 函数和未命名的命名空间可以避免它们。

坦率地说,如果我必须做出决定,我会完全删除它们:它们只会使语法混乱。

一个例子:The Most Vexing Parse

【讨论】:

  • @Matthieu,我已经提到了一个用例。仅供参考。
  • 首先,本地类与“Most Vexing Parse”没有关系。它们非常有用;真正的问题是您不能使用它们来实例化模板。
  • 就其本身而言,这个答案显然是错误的。毕竟,一旦决定支持本地类,额外的限制会使语法复杂化,而不是简化它。但是,结合其他答案,它很好地解释了为什么他们删除了该功能,而不是找到解决其缺点的方法。
  • “添加功能是有代价的” - 但目前它更像是一种反功能。也就是说,编译器必须检测并拒绝这种特殊情况,这也是有代价的。您还可以在标准“语法允许此类成员,但由于您无法定义它们通常无用”中添加非强制性注释。这样一个非功能的实现甚至比这个反功能更便宜。
  • @Dennis, @MSalters:但需要定义静态数据成员(通常在类范围之外),这需要另一个语法/语法规则。
【解决方案4】:

我认为这与阻止我们在模板实例化中使用本地类型的命名问题相同。

foo()::A::d这个名字并不是链接器解析的好名字,那么它应该如何找到静态成员的定义呢?如果函数 baz() 中有另一个结构体 A 怎么办?

【讨论】:

  • 没有说服力。毕竟,函数可以声明static 变量,但我们不想以foo()::static_var 的形式访问它。另外,我们甚至不想以foo()::A 的身份从外部访问本地类,那为什么要foo()::A::d
  • 这个论点成立,这确实是一个联系的问题。在函数中声明的变量具有 no 链接,即使它是 static 变量。但是对于 static 类方法的定义,必须匹配声明和定义,而这又确实需要链接。
【解决方案5】:

有趣的问题,但我很难理解您为什么想要本地类中的静态成员。静态通常用于跨程序流维护状态,但在这种情况下,使用范围为foo() 的静态变量不是更好吗?

如果我不得不猜测限制存在的原因,我会说这与编译器难以知道何时执行静态初始化有关。 C++ 标准文档可能会提供更正式的理由。

【讨论】:

  • C++ 标准几乎不包含任何理由;它通常出现在非规范性注释和 cmets 中。 (这是一个足够大的文档,仅包含规则;在 C++ 委员会的公开工作文档中可以找到合理数量的理由。)
【解决方案6】:

只是因为。

关于 C++ 的一个令人讨厌的事情是,它强烈依赖于“全局上下文”概念,其中所有内容都必须唯一命名。即使是嵌套的命名空间机制也只是字符串诡计。

我认为(只是一个疯狂的猜测)一个严重的技术问题是使用为 C 设计的链接器,并且只是进行了一些调整以使它们与 C++ 一起使用(并且 C++ 代码需要 C 互操作性)。

如果能够获取任何 C++ 代码并“包装”它以便能够在更大的项目中使用它而不会发生冲突,那就太好了,但由于链接问题,情况并非如此。我认为在函数级别禁止静态或非内联方法(甚至嵌套函数)没有任何合理的哲学理由,但这就是我们(目前)得到的。

即使是声明/定义的二元性及其烦人的冗长和暗示也只是关于实现问题(并且提供了在不提供源代码的情况下出售可用目标代码的能力,出于充分的理由,现在已经不那么流行了)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多