1

§14.8.2/4 allows the instantiation of two different functions, g<int> and g<const int> from the template definition. Why doesn't the Standard allow the definition of the two functions f in the code below? I know that both functions would have the same type void(int). But that also happens with the instantiated functions g. The note in §14.8.2/4 says: f<int>(1) and f<const int>(1) call distinct functions even though both of the functions called have the same function type..

#include <iostream>

template<typename T>
void g(T t) { std::cout << t << '\n'; }

void f(int i) { std::cout << i << '\n'; }
//void f(int const i) { std::cout << i << '\n'; }   // doesn't compile

int main()
{
    g<int>(1);
    g<int const>(2);
} 
Mao
  • 1,065
  • 8
  • 12

2 Answers2

5

Top-level consts on the parameter types are not part of the function signature. So the two versions of f() you've defined are the same function as far as overload resolution is concerned making the second one a redefinition.

From §13.1/3 [over.load]

— Parameter declarations that differ only in the presence or absence of const and/or volatile are equivalent. That is, the const and volatile type-specifiers for each parameter type are ignored when determining which function is being declared, defined, or called. [ Example:

 typedef const int cInt;
 int f (int);
 int f (const int); // redeclaration of f(int)
 int f (int) { /* ... */ } // definition of f(int)
 int f (cInt) { /* ... */ } // error: redefinition of f(int)

—end example ]
Only the const and volatile type-specifiers at the outermost level of the parameter type specification are ignored in this fashion; const and volatile type-specifiers buried within a parameter type specification are significant and can be used to distinguish overloaded function declarations.

Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • 1
    The real question is *why* are the top level `const`s on the parameters types not part of the function signatures? – R Sahu Jul 15 '14 at 18:10
  • 1
    @RSahu I'd imagine it's because the presence of a top-level cv-qualifier makes no difference to the caller, it is an implementation detail of the function. – Praetorian Jul 15 '14 at 18:11
  • I thinks so too. It will be great if some supporting document can be dug up from some place on the web. – R Sahu Jul 15 '14 at 18:13
  • @Praetorian Aren't the function signatures of the two instantiated functions `g` the same also? – Mao Jul 15 '14 at 18:13
  • @ChairmanMao: yes, in fact that's what's told by the note: *"even though both of the functions called have the same function type"*. But given this: `template void g(T t) { ++t; }`, then `g(42);` will compile while `g(42);` will not. – peppe Jul 15 '14 at 18:14
  • More info here: [C++ Type Deduction and Why You Care](http://vimeo.com/97344493), in this conferences is explained the diferent Type Deductions, C++98, C++11 and C++14, in this example is value type dedution (constness is throw away and T is deduced as int in both cases) – NetVipeC Jul 15 '14 at 18:15
  • @ChairmanMao In case of a function template, the two `g()`s for `T=int` and `T=const int` will be distinct, but their signatures will be the same. Meaning if you had a function pointer of the type `void(*)(int)` you could assign it the address of either `g` or `g` – Praetorian Jul 15 '14 at 18:21
0

The fact that top-level const is not part of the function signature allows for a minor advantage.

Suppose you have a function"

void f(int);

in its implementation, if you know you are not going to change the input parameter, you can declare:

void f(int const x) {
  std::cout << x << "\n";
}

and this is none of the business of the caller. Later, it turns out it would be useful to munge the input value (say, you want to treat negative integers as 0):

void f(int x) {
  if (x<0) x = 0;
  std::cout << x << "\n";
}

and without changing the signature or the rest of the body of the function, we are good to go.

Basically, the top level constness of arguments doesn't impact the usual binary calling conventions of C++, and logically the constness is no business of the caller. By eliminating that from the signature, we get some benefit.

For template functions, however, the types impact both the signature and the body of the function, and that body is part of the template's interface. (decltype lets the types of function parameters impact the body, but the body is not part of the interface like a template)

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524