为什么主函数的返回类型不能被推导?

33

如预期的那样,在C++11中以下代码会失败,因为该语言不支持常规函数的返回类型推导:

auto main()
{
   return 0;
}

然而,C++14可以做到,因此我无法解释以下错误(在GCC trunk、clang 3.8和Visual Studio 2015中具有相同的结果):

error: 'main' must return 'int'
在标准中是否有我没有看到的条款,禁止对main使用返回类型推导?还是两个编译器都不符合标准?(说实话,我从不这样做。int main()最好了...)

查看cppreference(http://en.cppreference.com/w/cpp/language/main_function):“7)(自C++14起)主函数的返回类型不能被推断(不允许使用auto main() {...)”。 - Amadeus
5
这个跟这个有关系吗?https://dev59.com/4GQn5IYBdhLWcg3wETg5#17135093 - Martin G
1
如果主函数中有多个return语句返回不同类型(int、double等)的数据,该怎么办? - Muhammad Ali
@MuhammadAli,这实际上是我想在我的答案中提到的一个好观点。但是对于普通函数来说,这也会失败。因此,main()在这方面可能不例外。顺便说一句,这个问题似乎是Will automatic return type deduction work for main?的确切重复,而不仅仅是相关的。 - iammilind
2
@iammilind:我不会与重复内容争论,但它并不是“完全相同”。首先,运行差异比较:你会发现有很多字符不同。更广泛地说,一个问题是在C++14中main函数的返回类型是否会被推断出来...而另一个问题则从答案应该是“是”的前提开始,问为什么各种编译器给出的结果与该答案相矛盾。它们有漏洞,还是有其他因素在起作用?不幸的是,这个前提是错误的,因为我错误地使用了过时的草案,所以这一切都有点无意义。 - Lightness Races in Orbit
显示剩余2条评论
5个回答

24
阅读C++17草案§3.6.1/2:(链接)
 

...并且它必须具有声明的返回类型为int的类型,...

所以我认为禁止使用推导是正确的。
上一版的C++14草案 (与C++17草案相同的部分)几乎完全相同:
 

它应具有声明的返回类型为int的类型,...


个人的反思,基于对评论和其他答案的阅读。不允许使用返回类型推导的原因是(我认为)编译器在看到return语句之前无法知道返回类型。还不少见到其他类型(可以隐式转换为int)可能被返回,这将使得推导的类型错误。预先声明返回类型(通过传统的方式或使用尾随返回类型)将在函数声明时设置类型,并可由编译器立即检查正确性。
至于允许使用类型别名,则只是一种类型的别名。因此,例如允许:
typedef int my_type;
my_type main() { ... }

真的与没有什么不同

int main() { ... }

所以在这段代码中,声明的返回类型是 MyType 而不是 int: typedef int MyInt; MyInt main() { },但是clang和gcc都接受。有什么原因吗? - Johannes Schaub - litb
4
N4296 不是“最后的C++14草案”,它是包含折叠表达式等新特性的第一个C++17草案。C++14 DIS 是 N3937(N3936 内容相同,但封面不同);IS 是 N4141(N4140 是 IS 加上编辑修正)。 - T.C.
1
@T.C. 这不是 isocpp.org 网站 上所说的:“这个工作草案包含了 C++14 标准以及一些小的编辑修改。” - isanae
1
如果有人使用推特,可以发送给@isocpp,让他们知道这个错误。 - M.M
1
关于理由,Richard Smith在这条评论中提到的内容是相关的。 - T.C.
显示剩余7条评论

17

从3.6.1/2(重点在我这里):

  

[...]它应该有一个类型为int声明返回类型,但其它方面它的类型是实现定义的。

当使用不带尾随返回类型的auto时,函数的声明返回类型仍然是auto,即使推导返回类型可能是其他类型。标准没有明确解释声明推导之间的区别,但7.1.6.4/7可能会有所启示:

  

当[...]在具有占位符类型的返回类型声明的函数中出现return语句时,推导的返回类型[...]是从其初始化器的类型确定的。对于没有操作数或操作数类型为voidreturn,声明的返回类型应为auto,推导的返回类型为void

我的理解是:

auto main(){ return 0; }

尽管推导出的返回类型int,但声明的返回类型仍将是auto。根据上文的3.6.1/2,main函数的声明的返回类型必须int。因此,这是不合法的。

然而,后置返回类型被认为是一个声明的返回类型。根据7.1.6.4/2:

如果函数声明符包括一个后置返回类型(8.3.5),那么该后置返回类型指定了函数的声明返回类型。

$ cat a.cpp
auto main() -> int {}
$ g++ -Wall -std=c++14 a.cpp
$

所有引用在C++14和C++17中均相同。


2
真正的问题是为什么从返回语句推断出的类型不能指定函数声明的返回类型。 - Random832
1
@Random832 我已经更新了我的回答,尽管没有真正的答案来解释“为什么”。 - isanae

8

从3.6.1 [basic.start.main]开始

1 程序必须包含一个名为main的全局函数,它是程序的指定起点....
2 实现不应该预定义main函数。此函数不应该被重载。它应该有一个声明的返回类型为int,但是其类型是由实现定义的...

如果标准限制推导,那么我认为“声明的返回类型int”这个措辞就是它。


这是我手头上的N4296草案中C++14的措辞。 - AndyG
啊,你说得对 - 我的草稿太旧了。我以为那是FDIS。:( - Lightness Races in Orbit

4
许多答案已经很好地提到了标准中的引用。但是,对于auto作为返回类型还存在另一个微妙的问题。
根据C++标准(某处),在main()内部,return语句不是必需的。这在Bjarne Stroustrup的网站中有明确说明:

在C++中,main()不需要包含显式的return语句。在这种情况下,返回的值是0,表示成功执行。

这意味着以下语句是有效的:
auto main () {}

可以假设在 } 前有一个隐含的 return 0; 语句。因此,在这种情况下,auto 被解释为 int。然而,根据 C++14 的技术性,由于没有返回语句,auto 必须被推导为 void!所以,“int vs void”,该考虑什么呢?
我认为这是一个警告,它从逻辑上防止了将 auto 作为返回类型。

这意味着下面的语句是有效的。我不明白你的推理。显然,如果没有返回语句,你不能使用返回类型推断。这绝不意味着当你编写返回语句时就不能使用返回类型推断,就像任何其他函数一样。 - Lightness Races in Orbit
@Light,你写道:“显然,如果没有返回语句,就无法使用返回类型推断。” 这并不正确。当然我们可以在没有return语句的情况下推导出return类型!演示。当没有返回类型时,它被推断为void,如图所示。你剩下的句子含义不清。无论如何,你可以尝试遵循链接中的重复代码,如果这样有帮助的话。 - iammilind
剩下的句子很清楚,但是如果不知道你困惑的部分,我无法帮助。 - Lightness Races in Orbit

3

正如在各个评论中所讨论的那样,我确实在标准中遗漏了它,因为我认为的 C++14 FDIS 副本实际上并不是(而是稍早的草案),并且在 CWG 1669 之后,"declared" 这个词被悄悄地加入到相关段落中。


1
这并没有提供问题的答案。如果要批评或请求作者澄清,请在他们的帖子下留言。-【来自审查】 - miken32
6
当然是一个答案,我不太可能向自己请求澄清。 - Lightness Races in Orbit
1
1669 找到不错的东西了。 - isanae
5
这个问题在 这个回答 中也有提到。如果我足够勇敢,我会将此问题标记为重复 :) - isanae
1
@iammilind:是的,正如一个月前讨论的那样,这肯定是一个答案。你有什么有用或建设性的东西要补充吗? - Lightness Races in Orbit
显示剩余2条评论

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