Why Doesn't the Visual Studio 2010 Debugger See static const Class Members?
这个问题与随后提出的问题密切相关。
类常量的定义方法在这里由stroustrup描述。
当我遵循stroustrup的方法时,我看到了预期的结果。但是,在Visual Studio 2010中,调试器无法解析该类范围内的static const类成员。我的意思是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <iostream>
class Foo {
public:
static const int A = 50;
char arr[A];
void showA();
};
void Foo::showA() {
std::cout <<"showA =" << A <<"
";
}
int main() {
Foo f;
f.showA();
} |
当调试器在showa()中时,"watch"窗口报告:
1
| Error: Symbol"Foo::A" not found |
我要强调的是,程序的行为确实如预期,即输出是:
程序返回0。
其他人能用Visual Studio 2010复制这个吗?这是调试器中的错误吗?
- 注意,您缺少A静态数据成员的定义。
- 它是内联的,到了操作点。
- 是否启用优化?类是否在命名空间中?
- @Scottjones:没关系,命名空间范围的定义仍然需要存在。
- @安迪如果是内线的话就不会。正如stroustrup文章和dauphic的答案所指出的,静态定义是可选的。我只是在vs中测试了两种方法,而在内联初始化中不需要静态def'n。
- @苏格兰人:请看我的答案。我引用了C++ 11标准。
- @谢谢你的澄清。我不确定线程是否建议op需要静态定义,或者只是为了调试器的可见性。在我看来,调试程序可能更智能。
- @史考特:好吧,我试着重新表述我的答案,让它更清楚。
- @斯科特乔内斯:对不起,我错了,你是对的(详情见我回答的评论)。我更新了答案。
- @再次感谢-所以最好在这里
可以在全局命名空间范围中添加静态数据成员的定义:
添加静态数据成员定义(这不是必需的,但允许)似乎可以解决您的问题。
我在使用VS2010的调试版本上对此进行了测试,并且当存在定义时,A的值会正确显示在调试窗口中(当缺少定义时会报告错误消息,与您提到的一致)。
- 感谢您引用本标准。这个问题的答案是111票以上,说明"类定义▼显示中的初始化只允许使用整型和枚举类型"。而且,stroustrup文章似乎暗示,只有在需要访问成员地址时才需要全局定义。再加上代码"按预期工作"与vs10和gcc/cygwin一起使用的事实,我敢打赌很多人不会意识到仍然需要全局定义。+ 1
- @用户2141130:很高兴能帮上忙,事实上还不清楚是否需要定义。
- 我添加了一个您可能想查看的更新。
- 未使用ODR;程序正常。但没有定义供调试器查找。
- @ USE2141130:这个答案是在C++ 11改变规则之前发布的——现在其他类型可以在类内初始化。stroustrup关于不总是需要全局定义的声明仍然是正确的。
- @ BenVoigt:好的。我必须承认我还没有具体地破解3.2p3,所以我对使用ODR的理解是有限的。我相信你并编辑答案,但如果你能稍微解释一下3.2p3的哪一部分使A不是odr(我理解这与"除非"部分有关,但第一段对我来说就是困惑)。
- 这句话,对吧?"如果一个变量的名称出现在一个可能被计算的表达式中,除非它是一个满足常量表达式(5.19)中出现要求的对象,并且立即应用lvalue到rvalue的转换(4.1),"我们知道Foo::A满足常量表达式中出现的要求,因为它是一个实际上是这样——它用作数组边界,这需要一个常量表达式char arr[A]。剩下的是其他用法cout << Foo::A是否立即使用lvalue-to-rvalue转换。
- 我们可以在27.7.3.6.2中看到这个签名operator<<(int val);,从中我们得出它是按值传递的(用法是右值),不受左值引用或类似的东西的约束。
- @本沃伊特:对。所以我想说,它确实使用了lvalue-to-rvalue转换,因为选择了一个int的operator <<的过载。
- @事实上,我不确定的是它是否满足以常量表达式出现的要求。我意识到使用它作为数组大小的事实一定意味着,但是我很困惑,因为A不是constexpr,而是const。实际上让我吃惊的是,它可以用作数组大小
- 常积分表达式早于constexpr的存在。(当然,规则现在解释了它,但他们不能要求它,因为这将打破大量现有的有效C++ 98代码)。规则见5.19。
- @好的,谢谢你的解释
- 我之前添加的更新被删除,建议在单独的问题中进行询问。这是个问题
这不是臭虫。编译器可以(几乎总是)优化静态常量的基本类型。编译器不为A分配存储,而只是将A的值嵌入编译的指令中。
因为A没有存储在任何地方,所以它没有地址,所以调试器无法查看它。
- 这里有一个bug,尽管它的值不是内联的。即使每次使用都是内联的,编译器实际上还是会发出一个定义,不管代码是否包含一个(即bug)。然后链接器删除定义,因为没有任何东西依赖于它的存在。
Visual C++错误地提供了一个弱定义(在这个答案中提供的证据),基于该类中的声明,尽管标准中有明确的语言:
The declaration of a static data member in its class definition is not a definition and may be of an incomplete type other than cv-qualified void. The definition for a static data member shall appear in a namespace scope enclosing the member’s class definition. In the definition at namespace scope, the name of the static data member shall be qualified by its class name using the :: operator.
根据标准中的另一条规则,如果成员不使用ODR,则不需要定义。
然而,VisualC++中是否有一个明确的定义或弱定义没有区别。如果该成员未使用ODR,则链接器将看不到对它的任何引用,并将删除它,使调试器对它是否存在感到困惑。使用Microsoft Linker,您可以使用/OPT:NOREF来抑制这种优化。
最终,这不是您想要在生产代码中做的事情,但是,因为您将拥有应用程序中遗留下来的标准库中的各种残留内容。但对于调试期间的临时使用,这是一个合理的设置。