Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Surprise in template parameter substitution?

N3690, § 14.8.2 paragraph 3 has this pretty mind blowing example:

template <class Z> void h(Z, Z*);
// #5: function type is h(int, const int*)
h<const int>(1,0);

Question: why is it not h(const int, const int*)?

From what know, Z = const int, so every occurence of Z in the template declaration can be read as const int, or am I missing something? Why pointer is different? I remember that when parameter has T& or T* it preserves cv-qualifiers of T, but I don't see any possibility to apply it here.

like image 213
Incomputable Avatar asked May 15 '17 13:05

Incomputable


2 Answers

You need to see [dcl.fct]/5 for the reason why:

A single name can be used for several different functions in a single scope; this is function overloading (Clause 13). All declarations for a function shall agree exactly in both the return type and the parameter-type-list. The type of a function is determined using the following rules. The type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type “array of T” or “function returning T” is adjusted to be “pointer to T” or “pointer to function returning T,” respectively. After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function’s parameter-type-list. [ Note: This transformation does not affect the types of the parameters. For example, int()(const int p, decltype(p)) and int()(int, const int) are identical types. —end note ]

And in const type* the const is not a top level const qualifier.

like image 81
NathanOliver Avatar answered Nov 14 '22 05:11

NathanOliver


The reason is another standard paragraph:

[dcl.fct/5]

A single name can be used for several different functions in a single scope; this is function overloading. All declarations for a function shall agree exactly in both the return type and the parameter-type-list. The type of a function is determined using the following rules. The type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T”. After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function's parameter-type-list. [ Note: This transformation does not affect the types of the parameters. For example, int()(const int p, decltype(p)) and int()(int, const int) are identical types. — end note ]

The rationalization for this is that to the caller it makes no difference if the parameter is cv-qualified; the steps to call the function as well as the conversion sequences for overload resolution are identical. So retaining const can in fact lead to ambiguity.

The cv-qualifier is in fact an implementation detail of the function, and has meaning only at the site of the functions definition (you won't be able to modify the parameter inside the function body).

like image 10
StoryTeller - Unslander Monica Avatar answered Nov 14 '22 05:11

StoryTeller - Unslander Monica