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

时间:2011-04-29 08:22:56

标签: c++

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

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

$ ./t.exe
Calling barney::barney()
Calling foo::operator()()
Calling barney::barney()

但它完全无法在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

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

这是:

#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;
}

输出:

Calling barney::barney()
Calling foo::operator()()
Calling barney::barney()

但我希望如此:

Calling barney::barney()
Calling barney::barney()

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

除了接受的答案外,David Rodriguez还提供了excellent answer detailing where it says in the standard that I'm allowed to declare the member named fred of struct fred

4 个答案:

答案 0 :(得分:6)

因为在fred结构中,您有一个成员fred(类型为foo),它会影响struct fred的定义。然后当你这样做:

return fred();

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

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

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

return ::fred();

答案 1 :(得分: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;

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

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

答案 2 :(得分:3)

当您致电f.memfunc时,对fred()的调用将解析为fred类型的foo成员。将memfunc声明为返回struct fred对你没有任何帮助,因为你不能对C ++中的返回值进行多态化。

要使fred()调用解析为struct fred的构造函数,请使用命名空间(::fred())限定调用。

答案 3 :(得分:1)

正如其他人所指出的那样,您正在呼叫成员operator()上的fred。更改成员的名称,或使用::fred()引用全局范围中的结构fred