函数返回类型的前置声明

13

正如这篇答案所建议的那样,我知道在函数声明中允许使用不完整类型作为返回值。所以我写了以下代码:

Obj.h

class Obj {
    int x;
};

f.h

class Obj;
Obj f();

f.cpp

#include "Obj.h"

Obj f() {
    return Obj();
}

main.cpp

#include "f.h"
int main() {
    f();
    return 0;
};

使用以下编译命令:

g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)

编译此代码。

g++ *.cpp

显示以下错误:

main.cpp: In function 'int main()':
main.cpp:4:7: error: invalid use of incomplete type 'class Obj'
     f();
       ^
f.h:1:7: error: forward declaration of 'class Obj'
 class Obj;
       ^

编译器不允许在函数声明中使用不完整类型作为返回值。这是为什么呢?


1
你是否将f.cpp和main.cpp一起编译了? - NathanOliver
@NathanOliver 我已经编辑了编译命令。是的,我把它们一起编译了。 - Hanna Khalil
3个回答

16

正如你自己所说,“在函数声明中允许使用不完整类型作为返回值”。这正是编译器允许你做的。你在非定义函数声明中成功使用了一个不完整的返回类型 - 你在f.h中对f的声明可以编译而没有任何问题。

但是这就是你被允许做的全部。这并不以任何方式改变以下事实:

  1. 在函数定义的时候,返回类型必须是完整的
  2. 在函数调用的时候,返回类型也必须是完整的。

在你的代码中,在main()内部,你试图调用具有不完整返回类型的函数。因此会出现错误。

5.2.2 函数调用 [expr.call]

10 如果函数调用结果类型是左值引用类型或函数类型的右值引用,则函数调用是左值; 如果函数调用结果类型是对象类型的右值引用,则函数调用是xvalue; 否则则函数调用是prvalue。

11 如果函数调用是对象类型的prvalue:

- 如果: - 函数调用是decltype-specifier的操作数,或者 - 函数调用是decltype-specifier的操作数的逗号运算符的右操作数, 则不会为prvalue引入临时对象。prvalue的类型可以是不完整的。[...]

- 否则,prvalue的类型必须是完整的。

换句话说,你被允许提前使用不完整的返回类型来声明函数。但是当你到达定义该函数或调用它的时候,你必须将返回类型完成。


4
问题在于main.cpp不知道Obj是什么,所以当它编译main.cpp时,无法调用f,因为返回类型是不完整的。您需要在main.cpp中引入Obj的定义。您可以通过在main.cpp中使用#include "obj.h"来实现这一点。 示例链接

我知道包含Obj.h文件可以解决这个问题。但问题是关于函数返回类型的前向声明。我在问题中提供的参考资料表明这样做是可以的。 - Hanna Khalil
2
@HannaKhalil 没问题。问题出现在你使用该函数时。当你使用它时,需要定义返回类型。 - NathanOliver

1
正如您所见,不是所有的不完整类型都被允许。事实上,规则是函数可以返回指向不完整类型的指针或引用。原因在于,在调用点时,编译器必须能够生成处理返回对象的代码。当没有关于对象内容的信息时,编译器无法生成代码。例如,假设Obj有一个非平凡析构函数;如果编译器不知道这一点,它就不能生成销毁对象的代码。当返回类型是指针或引用时,编译器拥有所有需要的信息:指针和引用通常不依赖于目标对象的细节。

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