返回类型是函数签名的一部分吗?


70

在C ++中,返回类型是否被视为函数签名的一部分?并且仅修改返回类型就不允许重载。

Answers:


87

普通函数的签名中不包含返回类型。

注意:我已经重写了此答案,并且以下注释不适用于此修订版-有关详细信息,请参见编辑历史)。

介绍

但是,标准中有关函数和函数声明的问题很复杂。必须考虑两层:

  • 声明书
  • 实体

所谓的功能声明可以声明功能实体或模板实体。如果声明了一个功能实体,那么您要么必须要明确定义功能模板(指定所有参数),要么要声明一个普通函数。如果声明了模板实体,那么您将声明一个主函数模板,或者未指定某些参数的显式专门化。(这与“对象声明”与对象或引用的关系非常相似:前者可以声明对象或引用。因此,对象声明不一定必须声明对象!)。

该标准在以下位置定义了功能的签名1.3.10

其参数的类型,如果函数是类成员,则为函数本身以及声明该成员函数的类的cv限定词(如果有)。功能模板特化的签名包括其模板参数的类型。(14.5.5.1)

正如(最近的C ++ 0x工作论文所指出的),此定义中缺少返回类型,该类型函数模板特化(即声明函数的功能声明,它是模板的特化)的签名的一部分。14.5.5.1修复了已经提到返回类型的问题1.3.10):

功能模板专业化的签名由功能模板的签名和实际模板参数(无论是显式指定还是推论)组成。

功能模板的签名包括其功能签名,其返回类型和模板参数列表。

那么,签名到底包含什么呢?

因此,当我们询问函数的签名时,我们必须给出两个答案:

  • 对于功能模板的专业化功能,签名包括返回类型。
  • 对于非专业功能,返回类型不属于签名。

但是请注意,无论如何,返回类型都是函数类型的重要组成部分。也就是说,以下无效:

void f();
int (*pf)() = &f; // different types!

如果仅返回类型不同,则重载何时无效?

当前主要编译器拒绝以下代码:

int f();
double f(); // invalid

但是接受以下代码:

template<typename T> int f();
template<typename T> double f(); // invalid?

但是,标准确实禁止仅在返回类型上有所不同的函数声明(当定义重载何时有效,何时无效)。但是,它并没有精确定义“仅按返回类型不同”的含义。


标准段落引用:

  • 什么时候可以重载函数声明: 13.1
  • 什么是函数声明:7/27/5
  • 功能模板/专业化的签名是什么: 14.5.5.1

作为参考,这是最新的C ++ 0x草案n3000关于中的“签名”的说法1.3.11,它在涵盖不同类型的实体方面更加完整:

函数的名称和参数类型列表(8.3.5),以及该函数所属的类或名称空间。如果功能或功能模板是类成员,则其签名还应包括功能或功能模板本身上的cv限定符(如果有)和ref限定符(如果有)。功能模板的签名还包括其返回类型和模板参数列表。功能模板特化的签名包括为其特化的模板的签名及其模板自变量(无论是明确指定还是推导)。[注意:签名用作名称处理和链接的基础。—尾注]


您能解释一下对函数static_cast <int(*)(char)>(foo <char>)('T')的强制转换吗?
yesraaj

1
yesraaj:foo <char>是不够的,因为有两个适合的函数,具有两种不同的返回类型。这些让编译器会将模板而不是T之后的类型无效(焦炭)和int(焦炭)
约翰内斯·绍布- litb

现在,为了获得正确的地址,您必须给编译器一个提示。它需要指针的类型。可以使用演员表来完成。您可以完成(int()(char))(foo <char>)而不是static_cast <int()(char)>(foo <char>)
Johannes Schaub-litb

但通常最好使用static_cast,因为它极大地限制了您可以强制转换为类型的范围。看到这个stackoverflow.com/questions/103512/...
约翰内斯·绍布- litb

现在,演员并不是提供背景的唯一可能性。你也可以做int(* ptr)(char)= foo <char>; ptr('T'); 并且编译器知道您需要返回int的模板函数。那你叫它
Johannes Schaub-litb

10

这取决于功能是否为功能模板

C ++模板-完整指南中,Jusuttis提供了与C ++标准中给出的定义不同的定义,但具有同等的结果:

我们将函数的签名定义为以下信息:

  1. 函数的非限定名称
  2. 名称名称空间范围,如果该名称具有内部链接,则声明该名称的转换单元
  3. constvolatileconst volatile功能的资格
  4. 功能参数的类型
  5. 它的返回类型,如果函数从函数模板生成
  6. 模板的参数模板参数,如果函数是从一个函数模板生成

正如litb所建议的,有必要澄清为什么返回类型是模板函数签名的一部分。

如果函数具有不同的签名,则它们可以共存于程序中。

。也就是说,如果返回类型是模板参数:

template <typename T>
T foo(int a)
{return T();}

可以实例化两个仅在返回类型上不同的函数:

foo<int>(0);
foo<char>(0);

不仅:正如litb所正确报道的那样,还可以重载两个模板函数,即使返回类型不是从属名称,它们也仅在返回类型上有所不同。这是他的例子:

template<class T> int foo(T)
{}

template<class T> bool foo(T)
{}

// at the instantiation point it is necessary to specify the cast
// in order not to face ambiguous overload

((int(*)(char))foo<char>)('a'); 

它在脚注中说:“此定义与C ++标准给出的定义不同,但其后果是相同的”。因此,这是某些书的定义,而不是C ++的定义。
Johannes Schaub-litb

如果您仔细阅读了这篇文章,则可以在第二句话中找到脚注:-)
Nicola Bonelli,

他们想指出的是,函数模板可以在相同的范围内共存:template <class T> int foo(T); template <class T> bool foo(T); int main(){(((int(*)(char))foo <char>)('a'); }
Johannes Schaub-litb

我认为您的答案在黑暗中有所启发。所以我投票了:)
约翰内斯·

我更改了答案以反映我对该标准的最新解释。
Johannes Schaub-litb

2

它们是该类型的一部分,您可以根据仅返回类型不同的函数指针类型来重载函数:

int IntFunc() { return 0; }
char CharFunc() { return 0; }

void FuncFunc(int(*func)()) { cout << "int\n"; }
void FuncFunc(char(*func)()) { cout << "char\n"; }


int main()
{
    FuncFunc(&IntFunc); // calls void FuncFunc(int_func func)
    FuncFunc(&CharFunc); // calls void FuncFunc(char_func func)
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.