在 c++ 中隐藏 int 变量的名称
Hiding name of int variable in c++
出于好奇,我尝试了这个代码,这是由面试问题[*]
int main(int argc, char *argv[])
{
int a = 1234;
printf("Outer: %dn", a);
{
int a(a);
printf("Inner: %dn", a);
}
}
当在Linux(g++ 4.6.3和clang++ 3.0(上编译时,它输出:
Outer: 1234
Inner: -1217375632
但是在Windows(VS2010(上,它打印:
Outer: 1234
Inner: 1234
基本原理是,在第二个"a"变量的复制构造函数完成之前,第一个"a"变量仍然可以访问。但是,我不确定这是标准行为,还是只是(其他(Microsoft怪癖。
知道吗?
[*] 实际问题是:
如何在不使用临时变量或全局变量的情况下,使用包含作用域中同名变量的值初始化作用域内的变量?
{
// Not at global scope here
int a = 1234;
{
int a;
// how do you set this a to the value of the containing scope a ?
}
}
如何在不使用临时变量或全局变量的情况下,使用包含作用域中同名变量的值初始化作用域内的变量?
除非可以显式命名外部作用域,否则无法执行此操作。可以显式命名全局作用域、命名空间作用域和类作用域,但不能显式命名函数或块语句作用域。
<小时 />C++11 [basic.scope.pdecl 3.3.2 p1 指出:
名称的声明点紧接在其完整声明符(第 8 条(之后和初始值设定项(如果有(之前,除非下文另有说明。[ 示例:
int x = 12; { int x = x; }
这里第二个 x 用它自己的(不确定的(值初始化。
MSVC 正确实现此示例,但是当初始值设定项使用括号而不是赋值语法时,它不会正确实现此示例。在Microsoft Connect上有一个关于此的错误。
这是一个示例程序,由于此错误,VS 中的行为不正确。
#include <iostream>
int foo(char) { return 0; }
int foo(int) { return 1; }
int main()
{
char x = 'a';
{
int x = foo(static_cast<decltype(x)>(0));
std::cout << "'=' initialization has correct behavior? " << (x?"Yes":"No") << ".n";
}
{
int x(foo(static_cast<decltype(x)>(0)));
std::cout << "'()' initialization has correct behavior? " << (x?"Yes":"No") << ".n";
}
}
C++包括以下注释。
[ 注意:涉及不确定值的操作可能会导致未定义的行为。
但是,此说明指出操作可能会导致未定义的行为,而不是它们必然会导致。上面链接的错误报告包括Microsoft确认这是一个错误,而不是程序触发未定义的行为。
编辑:现在我已经更改了示例,以便具有不确定值的对象仅在未评估的上下文中"使用",我相信这绝对排除了在任何平台上未定义行为的可能性,同时仍然演示了Visual Studio中的错误。
如何在不使用临时变量或全局变量的情况下,使用包含作用域中同名变量的值初始化作用域内的变量?
如果您想了解措辞的技术,这很容易。"临时"在C++中具有特定的含义(见§12.2(;您创建的任何命名变量都不是临时变量。因此,您可以创建一个使用正确值初始化的局部变量(不是临时变量(:
int a = 1234;
{
int b = a;
int a = b;
}
一个更可行的可能性是在外部作用域中使用对变量的引用:
int a = 1234;
{
int &ref_a = a;
int a = ref_a;
}
这根本不会创建一个额外的变量——它只是在外部作用域创建变量的别名。由于别名具有不同的名称,因此我们保留对外部作用域变量的访问权限,而无需定义变量(临时或其他方式(来执行此操作。许多引用在内部作为指针实现,但在这种情况下(至少在打开现代编译器和优化的情况下(,我希望它不会 - 别名实际上只是被视为引用外部范围内的变量的不同名称(并且使用 VC++ 的快速测试表明它以这种方式工作 - 生成的汇编语言根本不使用ref_a
(。
同样的可能性是这样的:
const int a = 10;
{
enum { a_val = a };
int a = a_val;
}
这有点类似于引用,只是在这种情况下,甚至没有争论a_val
是否可以称为变量的余地 - 它绝对不是一个变量。问题是枚举只能用常量表达式初始化,因此我们必须将外部变量定义为const
才能使其工作。
我怀疑这些都不是面试官的真正意图,但他们都回答了上述问题。第一个是(诚然(关于术语定义的纯技术性。第二个可能仍然对一些争论持开放态度(许多人认为引用是变量(。虽然它限制了范围,但对第三个没有质疑或争论的余地。
你正在做的,用自身初始化一个变量,是未定义的行为。你所有的测试用例都做对了,这不是一个怪癖。实现还可以将a
初始化为123456789
,并且它仍然是标准的。
更新:关于这个答案的评论指出,用自身初始化变量不是未定义的行为,但尝试读取这样的变量是。
如何在不使用临时变量或全局变量的情况下,使用包含作用域中同名变量的值初始化作用域内的变量?
你不能。 一旦声明了相同的名称,外部名称就无法在作用域的其余部分访问。 您需要外部变量的副本或别名,这意味着您需要一个临时变量。
我很惊讶,即使警告级别提高,VC++ 也没有抱怨:
int a(a);
视觉C++有时会警告您隐藏变量(也许这仅适用于派生类的成员(。 在初始化之前告诉您正在使用值通常也很好,这里就是这种情况。
查看生成的代码,它恰好将内部 a 初始化为与外部 a 相同的值,因为这是寄存器中留下的值。
我看了一下标准,它实际上是一个灰色地带,但这是我的 2 美分......
3.1 声明和定义 [basic.def]
声明
将名称引入翻译单元或重新声明先前声明引入的名称。
声明是一个定义,除非...[以下为非相关案例]
3.3.1 声明要点
- 非本地名称
名称的声明点紧接在其完整声明符之后和初始值设定项(如果有(之前,除非下面 [自赋值示例] 所述。
在声明隐藏它的本地名称之前仍然可见。
现在,如果我们假设这是内部"a"的声明点(3.3.1/1(
int a (a);
^
那么外部"A">应该在定义内部"A"的那一点(3.3.1/2(之前可见。
问题是在这种情况下,根据 3.1/2,声明是一个定义。这意味着应该创建内部的"a"。在那之前,我无法从标准中理解外部"a"是否仍然可见。 VS2010 假设它是,括号内的所有内容都是指外部范围。然而,clang++ 和 g++ 将该行视为自我分配的情况,这会导致未定义的行为。
我不确定哪种方法是正确的,但我发现VS2010更加一致:在完全创建内部"a"之前,外部范围仍然可见。
- 将布尔变量添加到 int
- 为什么 'main' 函数中的局部 int 变量会自动初始化?
- 在类 (C++) 之外设置 const int 成员变量
- 如何检查 int 变量是否包含合法(非陷阱表示)值?
- 将子字符串字符从字符串值转换为 int,然后将其分配给 int 变量
- 如何在传递给 C/C++ 宏之前解析 int 变量?
- 有什么方法可以使用 int 变量来完成组件名称吗?
- 将 static_cast<int>(-15) 分配给静态常量字符类型变量
- 在C++中,为什么int可以使用new运算符初始化变量,而double不能
- 什么是变量均值'int border = borderType & ~BORDER_ISOLATED'?
- 如何定义int[][26]类型的变量
- 在变量名后声明带有 () 的非内部类型与不使用变量名的行为不同。即 std::map<int,char>x(); - 这是怎么回事?
- 声明具有两种类型的变量:"int char"
- 有没有办法"QByteArray"变量数据直接放入变量"int"而无需强制转换?
- 在 C++ 中,当我声明一个整数变量 int a = 200L 或 int a = 200F 或 int a = 200
- 试图在C++中打印变量int会导致它崩溃.为什么?
- 非常量指针是否可以修改常量变量int c++
- 在初始化中使用新声明的变量(int x=x+1)
- 如何在数组中按变量(int-最小到最大)对数组进行排序
- 如果我想将字符提升为 int,我应该使用 static_cast(char 变量<int>)还是 +(char 变量),为什么?