这个问题如何在C++中实现?

17
  1. 令我惊讶的是,C++ 对象的名称可以与类名相同。有人能解释一下原因吗?
  2. 当我将类 a 的对象声明为 a a1() 时,它不会引发错误,但也没有调用构造函数。这是为什么?

我的代码:

#include<iostream>
using namespace std;

class a 
{
    public:
    a() 
    {
        cout << "in a\n";
    }
};

int main()
{
    a a1();
    a a;
}

15
a1 不是一个对象,而是一个函数。 - Luchian Grigore
4
这并不是“最令人烦恼的语法解析”。它只是一个函数声明。最令人烦恼的解析涉及到使用临时参数进行函数调用。 - Pete Becker
1
@PeteBecker 技术上是正确的,但我们没有单独的 [tag:somewhat-vexing-parse] 标签 ;) - fredoverflow
1
@FredOverflow - 虽然这个问题应该被关闭为重复,但它不应该链接到与问题无关的答案,即使没有合适的标签。 - Pete Becker
1
@PeteBecker 我怀疑我们不会找到一个涵盖他两个问题的重复内容。 - fredoverflow
显示剩余7条评论
5个回答

21
当你写下 a a1(); 的时候,实际上它被解析为一个函数声明而不是对默认构造函数的调用。
a a1; 则可以正确地调用默认构造函数。
当你写 a a; 时,它可以工作,因为变量名优先于类名,在所谓的名称隐藏中。尽管它可以工作,但只会导致混淆,我建议避免这样做。
对于所有喜欢标准引用的人,这里是一句话:
如果在同一作用域内(以任何顺序)声明具有相同名称的类或枚举类型和变量、数据成员、函数或枚举常量,则变量、数据成员、函数或枚举常量的名称可隐藏类或枚举类型名称,并使其在可见的位置隐含不发生作用。

2
“是的,它可以工作…”并不是一个解释。我希望看到的是:本地变量a的定义隐藏了类型名称,以避免与外部(包含)名称的干扰。 - Nobody moving away from SE
@Nobody 我不是很喜欢在每个简单的问题上都引用标准,而且那也不是主要问题。 - aaronman
2
我并没有说要引用标准(尽管拥有权威的明确答案总是很好的)。像我的评论中那样的一些推理就足够了。 - Nobody moving away from SE
@没有挑剔的人,让这个网站的质量保持高水平 :) - aaronman
@Nobody Answers 不仅应该为原帖提供答案,也应该为未来的读者提供答案,因此要求更完整的答案从来不是坏事。并不是每个人都感觉引用标准很舒服,这是可以理解的。 - Shafik Yaghmour

8

a a1(); 是一个函数声明。

这是在C++11中创建统一初始化的重要原因。要使用构造函数初始化对象,请使用 a a1{};


@PeteBecker,统一初始化也有它自己的缺陷,我们可以在这里看到:https://dev59.com/5nLYa4cB1Zd3GeqPUCaX。 - Shafik Yaghmour

4
事实上,使用变量隐藏类的名称是有效的。如果您查看C++draft standard section 3.3.10 Name hiding段落2,它说(重点在于我):“在同一作用域中声明的变量、数据成员、函数或枚举器的名称可以隐藏类名(9.1)或枚举名(7.2)。如果在同一作用域中以任何顺序声明了类或枚举名和变量、数据成员、函数或枚举器具有相同的名称,则无论变量、数据成员、函数或枚举器名称在何处可见,都会隐藏类或枚举名。”
我认为这不是一个好的做法,会导致难以维护的代码。这行代码实际上声明了一个函数:
a a1();

你也可以使用这个旧版本的C++11

a a1 ;

或者 统一初始化C++11 中引入:
a a1{} ;

回到“名称隐藏”这个话题,让我感到惊喜的是,无论设置了什么警告级别,clang 都会用下面的代码来警告你:
int main()
{
   a a;
   a a2 ;
}

我收到了这条消息:
main.cpp:12:10: note: class 'a' is hidden by a non-type declaration of 'a' here
   a a;
     ^

尽管我无法从gcc获得类似的警告。 更新 考虑到我之前在统一初始化的缺陷中所做的评论,我意识到如果你怀疑a1不是正确的类型,你可以使用typeid来调试发生了什么。例如,这段代码:
std::cout << typeid(a).name() << std::endl ;
std::cout << typeid(a1).name() << std::endl ;

Coliru 实时示例 中,会产生以下输出结果:

1a
F1avE

通过 c++filt 进行转换,你将得到以下输出:

a ()     // A function that returns type a
a        // type a

0

我尝试使用clang编译您的代码,得到了这个结果,我认为它已经说明了一切。

test.cpp:15:9: warning: empty parentheses interpreted as a function declaration
      [-Wvexing-parse]
    a a1();
        ^~
test.cpp:15:9: note: remove parentheses to declare a variable
    a a1();
        ^~
1 warning generated.

0

a a1(); 是一个函数声明,返回类型为 a,与调用构造函数无关。

a a; 是一个简单的语句,可以正常工作并调用构造函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接