tags:

views:

3913

answers:

3

In C++, is the return type considered part of the function signature? and no overloading is allowed with just return type modified.

+33  A: 

Normal functions do not include the return type in their signature.

(note: i've rewritten this answer, and the comments below don't apply to this revision - see the edit-history for details).

Introduction

However, the matter about functions and function declarations in the Standard is complicated. There are two layers that have to be considered:

  • Declarations
  • Entities

The so-called function declaration may declare a function entity or a template entity. If a function entity is declared, then you either have to do with an explicit specialization of a function template (with all arguments specified), or a declaration of an ordinary function. If a template entity is declared, then you are declaring a primary function template, or an explicit specialization where some arguments are not specified. (This is very similar to the relation of "object declaration" and objects or references: The former may declare either an object or a reference. So an object declaration may not necessarily declare an object!).

The Standard defines the signature of a function to include the following at 1.3.10:

The types of its parameters and, if the function is a class member, the cv- qualifiers (if any) on the function itself and the class in which the member function is declared. The signature of a function template specialization includes the types of its template arguments. (14.5.5.1)

It's missing the return type in this definition, which is part of the signature of a function template specialization (i.e a function declaration that declares a function which is a specialization of a template), as pointed out by 14.5.5.1 (recent C++0x working papers fixed that already to mention the return type in 1.3.10 too):

The signature of a function template specialization consists of the signature of the function template and of the actual template arguments (whether explicitly specified or deduced).

The signature of a function template consists of its function signature, its return type and its template parameter list.

So what exactly does a signature contain, again?

So, when we ask about the signature of a function, we have to give two answers:

  • For functions that are specializations of function templates, the signature includes the return type.
  • For functions that are not specializations, the return type is not part of the signature.

Notice, however, that the return type, in any case, is a significant part of the type of a function. That is, the following is not valid:

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

When is an overload invalid if only the return type differs?

Major compilers currently reject the following code:

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

But accept the following code:

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

However, the Standard does forbid a function declaration that only differs in the return type (when defining when an overload is valid, and when not). It does not define precisely what "differs only by return type" means, though.


Standard paragraph references:

  • When can a function declaration be overloaded: 13.1
  • What is a function declaration: 7/2 and 7/5
  • What is the signature of a function template/specialization: 14.5.5.1

For reference, here is what the most recent C++0x draft n3000 says about "signature" in 1.3.11, which is much more complete in its coverage of the different type of entities:

the name and the parameter type list (8.3.5) of a function, as well as the class or namespace of which it is a member. If a function or function template is a class member its signature additionally includes the cv-qualifiers (if any) and the ref-qualifier (if any) on the function or function template itself. The signature of a function template additionally includes its return type and its template parameter list. The signature of a function template specialization includes the signature of the template of which it is a specialization and its template arguments (whether explicitly specified or deduced). [ Note: Signatures are used as a basis for name mangling and linking. — end note ]

Johannes Schaub - litb
Can you explain this casted call to function static_cast<int(*)(char)>(foo<char>)('T');
yesraaj
yesraaj: foo<char> will not suffice, since there are two functions that would fit, with two different return types. these have types void(char) and int(char) after the compiler puts the template instead of T.
Johannes Schaub - litb
Now, in order to get the right address, you have to give the compiler a hint. It needs the type of the pointer. That can be done using a cast. You could have done (int(*)(char)) (foo<char>) instead of static_cast<int(*)(char)> (foo<char>)
Johannes Schaub - litb
but it's generally better to use static_cast, since it greatly restricts the domain of types you can cast to. See this http://stackoverflow.com/questions/103512/in-c-why-use-staticcastintx-instead-of-intx
Johannes Schaub - litb
now, a cast is not the only possibility to provide context. you can also do int(*ptr)(char) = foo<char>; ptr('T'); and the compiler knows you want the template-function that returns int. then you call it.
Johannes Schaub - litb
Johannes Schaub - litb
hope this helps you in trying to understand that line of code
Johannes Schaub - litb
+4  A: 

It depends if the function is a function template or not.

In C++ Templates -- the complete guides, Jusuttis provides a different definition of that given in the C++ standard, but with equivalent consequences:

We define the signature of a function as the the following information:

  1. The unqualified name of the function
  2. The class or namespace scope of that name, and if the name has internal linkage, the translation unit in which the name is declared
  3. The const, volatile, or const volatile qualification of the function
  4. The types of the function parameters
  5. its return type, if the function is generated from a function template
  6. The template parameters and the template arguments, if the function is generated from a function template

As litb suggested, it's worth to clarify why the return type is part of the signature of a template function.

Functions can coexist in a program if they have distinct signatures.

. That said, if the return type is a template parameter:

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

it's possibile to instantiate two function which differ only in the return type:

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

Not only: as rightly reported by litb, it is also possible to overload two template functions, which differ only in the return type, even if the return type is not a dependent name. Here's his example:

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');
Nicola Bonelli
It says in a footnote "This definition is different from that given by the C++ Standard, but its consequences are equivalent". Thus that is the definition of some book, but not of C++.
Johannes Schaub - litb
If you read this post carefully you can find the footnote in the second sentence :-)
Nicola Bonelli
The point they wanted to make is that function templates can coexist like this in the same scope:template<class T> int foo(T); template<class T> bool foo(T); int main() { ((int(*)(char))foo<char>)('a'); }
Johannes Schaub - litb
you are right it works with non dependent return type as well...
Nicola Bonelli
I think your answer put some light in the darkness. so i upvoted :)
Johannes Schaub - litb
I updated it with your example as well :-)
Nicola Bonelli
I've changed my answer to reflect my most recent interpretation of the standard.
Johannes Schaub - litb
A: 

They are enough a part of the type that you can overload functions based on function pointer types that differ only by return type:

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)
}
Eclipse