Reputation:
I am asking this question with reference to the following code
#include <iostream>
using namespace std;
class A {
void foo(){}
};
template <typename T>
void func(T (&a) [1]) {cout << "In array type" << endl;}
template <typename T>
void func(T (*a) [1]) {cout << "In pointer to array type " << endl;}
template <typename T>
void func(T* a) {cout << "in pointer type" << endl;}
template <typename T>
void func(T** a) {cout << "In pointer pointer type" << endl;}
template <typename T>
void func(...) {}
int foo(int a) {return 1;}
int foo1() {return 1;}
int main() {
A a[1];
func<A>(&a);
return 0;
}
What is the type of an array? From the following code I can tell that when you take the address of an array with the &
operator the function call resolves to the one with T (*a) [1]
and the call is not ambiguous, but the when I change the code to the following
#include <iostream>
using namespace std;
class A {
void foo(){}
};
template <typename T>
void func(T (&a) [1]) {cout << "In array type" << endl;}
template <typename T>
void func(T (*a) [1]) {cout << "In pointer to array type " << endl;}
template <typename T>
void func(T* a) {cout << "in pointer type" << endl;}
template <typename T>
void func(T** a) {cout << "In pointer pointer type" << endl;}
template <typename T>
void func(...) {}
int foo(int a) {return 1;}
int foo1() {return 1;}
int main() {
A a[1];
func<A>(a); // <-- CHANGE HERE
return 0;
}
I get an error, saying that the call to the function is ambiguous. So the imaginary type_of(a)
and T*
are equivalent. How then are the types of &a
and T**
not equivalent?
The way I am trying to explain this to myself is that the type of an array to objects is T (&) [N]
and the type of an array of objects of type T
is T (*) [N]
. And that the standard allows implicit conversion from the first (i.e. from T (&a) [N]
to T*
) but when you take the address of an array it does not implicitly go from a T (*) [N]
to a T**
. Am I correct? Or am I inferring what T (*) [N]
is wrongly?
Further how do you go about reading the meaning of syntax like this. Is there a document I can refer to?
Thanks!
Upvotes: 3
Views: 226
Reputation: 302932
... the type of an array to objects is
T (&) [N]
...
No. The type of an array is T[N]
. The type of a
is A[1]
. What you are describing is a reference to an array, and your original example involves passing a pointer to an array, which has type A(*)[1]
. The other rule you are running into is that an array can decay into a pointer. In other words, an object of type T[N]
can be implicitly converted to an object of type T*
.
It's because of that implicit conversion that your second example fails. The relevant overloads are simply:
template <typename T> void func(T (&)[1]); // (1)
template <typename T> void func(T* ); // (2)
Both overloads are viable. The first is an exact match, whereas the second involves an array-to-pointer conversion. But according to the table in [over.ics.rank], the way to determine which viable candidate is better involves selecting conversion sequences thusly:
Standard conversion sequence
S1
is a better conversion sequence than standard conversion sequenceS2
if
S1
is a proper subsequence ofS2
(comparing the conversion sequences in the canonical form defined by 13.3.3.1.1, excluding any Lvalue Transformation; the identity conversion sequence is considered to be a subsequence of any non-identity conversion sequence) or, if not that,the rank of
S1
is better than the rank ofS2
, orS1
andS2
have the same rank and are distinguishable by the rules in the paragraph below, or, if not that,[..]
The array-to-pointer conversion is an lvalue transformation, so it's excluded from that bullet point. Thus, neither conversion sequence is considered better, so we have to move onto the other tiebreakers. Yet, both functions are templates, and neither is more specialized than the other. That's why it's ambiguous.
Upvotes: 6