为什么这个程序调用operator()而不是构造函数?

Omn*_*ous 6 c++

这是一个在没有警告的情况下编译的程序,例如GNU C++:

$ g++ -o t -Wall -pedantic -Wshadow t.cpp

$ ./t.exe
Calling barney::barney()
Calling foo::operator()()
Calling barney::barney()
Run Code Online (Sandbox Code Playgroud)

但它完全无法在MSVC++上编译:

$ cl /EHsc t.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

t.cpp
t.cpp(17) : error C2380: type(s) preceding 'fred' (constructor with return type, or illegal redefinition of current class-name?)
t.cpp(17) : error C2208: 'fred' : no members defined using this type
Run Code Online (Sandbox Code Playgroud)

更重要的是,当它编译时,输出不是我所期望的.有人可以了解这段代码所需的标准行为吗?

这里是:

#include <iostream>

using ::std::cerr;

struct fred;

struct foo {
   inline fred operator ()();
};

struct barney {
   barney() : v_(0) { cerr << "Calling barney::barney()\n"; }
   int v_;
};

struct fred : public barney {
   foo fred;
   int joe;
   struct fred memfunc() { return fred(); }
};

inline fred foo::operator ()()
{
   cerr << "Calling foo::operator()()\n"; return fred();
}

int main(int argc, const char *argv[])
{
   fred f;
   f.memfunc();
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

它输出这个:

Calling barney::barney()
Calling foo::operator()()
Calling barney::barney()
Run Code Online (Sandbox Code Playgroud)

但我希望如此:

Calling barney::barney()
Calling barney::barney()
Run Code Online (Sandbox Code Playgroud)

为什么我得到输出?这是标准行为吗?如果是,为什么,标准的哪些部分是相关的?

除了公认的答案,大卫·罗德里格斯给了一个优秀的答案,详述它在我被允许申报命名该成员的标准说fredstruct fred.

dav*_*mac 6

因为在fred结构中你有一个阴影定义的成员fred(类型foo)struct fred.当你这样做:

return fred();
Run Code Online (Sandbox Code Playgroud)

... fred指的是类型的对象foo而不是fred结构类型,因此foo调用()运算符.

请注意,名称"fred"指的是两个不同的东西 - 成员,类型为foo,以及fred结构类型.编译器必须选择其中一个,并且它根据C++标准的3.4节("名称查找")中定义的规则执行此操作.

您可以强制fred使用命名空间限定来引用类型:

return ::fred();
Run Code Online (Sandbox Code Playgroud)

  • 未经测试,但我认为`return :: fred();`可能会成功.当然,最好的答案是没有一个名字与封闭类型相匹配的成员. (2认同)

Dav*_*eas 6

它上面应该生成一个错误部分的问题.不符合标准:

9.2 [class.mem]/13

如果T是类的名称,则以下每个名称都应具有与T不同的名称:

  • T类的每个静态数据成员;
  • 类T的每个成员函数[注意:此限制不适用于没有名称(12.1)]的构造函数;
  • T类的每个成员本身就是一个类型;
  • T类每个成员的每个枚举器都是枚举类型; 和
  • 作为T类成员的每个匿名联盟的每个成员.

9.2 [class.mem]/13a

此外,如果类T具有用户声明的构造函数(12.1),则类T的每个非静态数据成员都应具有与T不同的名称.

至于它为什么找到成员而不是变量,这与在C++中如何处理标识符非常一致,其中有两个标识符空间,一个用于用户定义的类型,另一个用于其他元素(包括typedef) ):

struct test {};
void test() {}
// or (but not both)
// int test;
Run Code Online (Sandbox Code Playgroud)

使用这两个定义,test指的是函数(或变量),以及struct test用户定义的类型.这是一个特殊的极端情况,在C语言中typedef声明使用时struct会在C++中产生差异,因为它会在公共标识符空间中注入名称:

typedef struct test {} test; // now "test" is in both spaces
// void test() {}            // error: redefinition of "test"
Run Code Online (Sandbox Code Playgroud)