Reputation: 491
tl;dr;
why is - regarding overload resolution / function declaration - the pointer type
treated as a more equally exact match than the array type
, even though a passed variable is really of type array type
and not "just" of pointer type
I already looked at some of the equivalent questions (e.g. 21972652, 29602638 and 72929287) regarding overload resolution and array-to-pointer decay and learned that the compiler will treat void foo(int (&a)[3]);
and void foo(int* a);
for the same when passing in int bar[3];
, but I do not really understand why it does so (despite the fact that it is stated to do so in the specification).
Consider the following example, which does not compile due to ambiguous function call (online):
#include <iostream>
#include <utility>
void foo(int (&a)[2])
{
std::cout << "Called concrete foo with N = 2";
}
void foo(int* a)
{
std::cout << "Called pointer foo" << std::endl;
}
int main()
{
int a1[] = { 0x00 };
int a2[] = { 0x00, 0x01 };
int a3[] = { 0x00, 0x01, 0x02 };
foo(a1);
foo(a2);
foo(a3);
}
My naive understanding tells me, that for the function call foo(a2)
, int (&a)[2]
should be more concrete / exact regarding overload resolution than int* a
, because the actual type of the variable a2 is int[2]
(array type) and not int*
(pointer type).
So why does the compiler not favor void foo(int (&a)[2])
over void foo(int* a)
?
Kind regards
this first part of the question (why does the compiler not favor void foo(int (&a)[2])
over void foo(int* a)
) is already answered and marked as accepted, since this was my initial question, nevertheless it would be nice if someone could explain some reasoning behind this decision, i.e. why it is the way it is and whether there is any counter example (besides backward compatibility) to favor void foo(int (&a)[2])
over void foo(int* a)
.
Upvotes: 2
Views: 287
Reputation: 10700
@463035818_is_not_a_number already explained in their answer why void foo(int* a);
is a better match than template<std::size_t N> void foo(int (&a)[N])
.
This answer only applies to the second example in the question:
void foo(int (&a)[2]);
void foo(int* a);
// why is this call ambiguous?
int arr[2];
foo(arr);
Both functions can be found by name lookup, are candidate functions and viable functions.
So which function will be called (or if the call is ambiguous) is based on wether any of them is a better viable function than the other.
As per 12.4.3 Best viable function [over.match.best] (2):
(2) Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
[...]
(2.1) for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), [...]
So to determine if any of the two functions is better we need to check the conversion sequences of their arguments and compare them.
Let's first determine which conversion sequences we need for both calls:
void foo(int (&a)[2]);
(1) When a parameter of reference type binds directly to an argument expression, the implicit conversion sequence is the identity conversion [...]
void foo(int* a);
(1) An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue of type “pointer to T”. [...]
To determine which conversion sequence is better we need to reference 12.4.3.2 Ranking implicit conversion sequences [over.ics.rank] - in this case mainly paragraph (3) and (4).
(1) This subclause defines a partial ordering of implicit conversion sequences based on the relationships better conversion sequence and better conversion. If an implicit conversion sequence S1 is defined by these rules to be a better conversion sequence than S2, then it is also the case that S2 is a worse conversion sequence than S1. If conversion sequence S1 is neither better than nor worse than conversion sequence S2, S1 and S2 are said to be indistinguishable conversion sequences.
Note: we'll skip (3.1) because it only applies to list-initialization.
So let's start with the first condition, (3.2.1):
(3.2) Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if:
- (3.2.1) S1 is a proper subsequence of S2 (comparing the conversion sequences in the canonical form defined by [over.ics.scs], excluding any Lvalue Transformation; the identity conversion sequence is considered to be a subsequence of any non-identity conversion sequence) or, if not that, [...]
Note: Due to questions about this clause in the comments i'll explain this rule in detail.
tl;dr: Neither conversion sequence is a subsequence of the other.
To fully understand this rule we need to first define a few terms:
A
and B
, if A
is a subsequence of B
, and A
is not equal to B
, then A
is a proper subsequence of B
. ()(A, B)
is a proper subsequence of (A, B, C, D)
(we can remove elements)(B, C)
is a proper subsequence of (A, B, C, D)
(removing is allowed at any position)(A, B, C)
is not a proper subsequence of (A, C, B, D)
(order matters)(A)
is not a proper subsequence of (A)
(if both sequences are equal, neither is a proper subsequence of the other)()
is a proper subsequence of (A, B, C)
(the empty sequence is a proper subsequence of all non-empty sequences)()
) - applying no conversions to a value always results in the original value: value ∘ () = value
So with this we can break down the (3.2.1)
rule:
comparing the conversion sequences in the canonical form defined by [over.ics.scs], excluding any Lvalue Transformation
the identity conversion sequence is considered to be a subsequence of any non-identity conversion sequence
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if:
- S1 is a proper subsequence of S2
So now let's apply this to our specific case:
foo(int (&a)[2])
: ()
(identity conversion sequence)foo(int* a);
: ("Array-to-pointer conversion")
Now we need to remove Lvalue-conversions. In 12.4.3.1.1 Standard conversion sequences [over.ics.scs] (3) Array-to-pointer conversion is listed as an Lvalue-conversion, therefore we must remove it from S2.
So S2 = ()
Now we need to check if S1 is a proper subsequence of S2.
It can't be a proper subsequence because S1 = S2, therefore this rule does not apply.
Next we need to check the rank of the conversion sequences as per (3.2.2)
(3.2.2) the rank of S1 is better than the rank of S2, or S1 and S2 have the same rank and are distinguishable by the rules in the paragraph below, or, if not that,
There are three possible ranks for conversions: Exact Match, Promotion or Conversion, as per 12.4.3.1.1 Standard conversion sequences [over.ics.scs] (3):
(3) Each conversion [...] also has an associated rank (Exact Match, Promotion, or Conversion). These are used to rank standard conversion sequences. The rank of a conversion sequence is determined by considering the rank of each conversion in the sequence and the rank of any reference binding. If any of those has Conversion rank, the sequence has Conversion rank; otherwise, if any of those has Promotion rank, the sequence has Promotion rank; otherwise, the sequence has Exact Match rank.
Conversion Category Rank No conversions required Identity Exact Match Lvalue-to-rvalue conversion Lvalue Transformation Exact Match Array-to-pointer conversion Lvalue Transformation Exact Match Function-to-pointer conversion Lvalue Transformation Exact Match Qualification conversions Qualification Adjustment Exact Match Function pointer conversion Qualification Adjustment Exact Match Integral promotions Promotion Promotion Floating-point promotion Promotion Promotion Integral conversions Conversion Conversion Floating-point conversions Conversion Conversion Floating-integral conversions Conversion Conversion Pointer conversions Conversion Conversion Pointer-to-member conversions Conversion Conversion Boolean conversions Conversion Conversion
The ordering of which is as follows:
12.4.3.2 Ranking implicit conversion sequences [over.ics.rank] (4)
(4) Standard conversion sequences are ordered by their ranks: an Exact Match is a better conversion than a Promotion, which is a better conversion than a Conversion. Two conversion sequences with the same rank are indistinguishable unless one of the following rules applies: [...]
(there are also a bunch of exceptions listed in that paragraph, but none of them apply to the given example)
So from the above table:
So both conversion sequences have the "Exact Match" rank, so we need to check if one of the rules below (3.2.2) applies to our case:
(3.2.3) S1 and S2 include reference bindings [...]
(3.2.4) S1 and S2 include reference bindings [...]
Only one overload of foo
binds by reference, therefore both subclauses don't apply.
(3.2.5) S1 and S2 differ only in their qualification conversion [...]
Neither S1 nor S2 contain a qualification conversion, therefore this subclause doesn't apply.
(3.2.6) S1 and S2 include reference bindings [...]
Doesn't apply - see reasoning for (3.2.3) / (3.2.4)
So there are unfortunately no subclauses that would make the two conversion sequences distinguishable.
The only paragraph left from [over.ics.rank] is (3.3) - which deals with user-defined conversion sequences.
So we've run out of paragraphs that could resolve the ambiguity - therefore the conversion sequences required for the two functions are indistinguishable conversion sequences.
Due to the conversion sequences of both foo
s being indistinguishable, neither function is a better viable function.
So the second part of 12.4.3 Best viable function[over.match.best] (3) applies:
If there is exactly one viable function that is a better function than all other viable functions, then it is the one selected by overload resolution; otherwise the call is ill-formed.
So the call is ill-formed and will result in an ambiguous function call error.
There is an open core language issue for exactly this case from 2013: CWG 1789
The current rules make an example like
template<class T, size_t N> void foo(T (&)[N]); template<class T> void foo(T *t); int arr[3]{1, 2, 3}; foo(arr);
ambiguous, even though the first is an identity match and the second requires an lvalue transformation. Is this desirable?
with a proposed solution from 2021, that suggests adding another bullet to 12.4.3.2 Ranking implicit conversion sequences [over.ics.rank] (3) in the form of:
S1 is a reference binding to an array and S2 is an array-to-pointer conversion (7.3.3 [conv.array]).
[Example 7:template<class T, unsigned N> void f(T (&)[N]); // #1 template<class T> void f(T *t); // #2 int r[3]{1, 2, 3}; void g() { f(r); // OK: calls #1 }
—end example]
This would make the conversion sequence for void foo(int (&a)[2]);
better than the one of void foo(int* a);
and therefore resolve the ambiguity. (void foo(int (&a)[2]);
would always be called)
Unfortunately it is still in the Review state, so it is unclear when (or even if) it will be included in the standard.
There is a simple way to avoid the ambiguity: Make void foo(int*)
a template (non-templated functions are preferred over templated ones).
So this set of functions would not be ambiguous:
void foo(int (&)[2]) {}
template<class = void>
void foo(int*) {}
Another option would be to always use std::array
- that also avoids most of the common pitfalls of c-style arrays. (What's wrong with arrays?)
Upvotes: 3
Reputation: 1
So why does the compiler not favor void
foo(int (&a)[2])
over voidfoo(int* a)
?
Because the array to pointer conversion is not considered worse than the identity conversion. This can be seen from ics.rank-3.2.1 which states:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if
- S1 is a proper subsequence of S2 (comparing the conversion sequences in the canonical form defined by [over.ics.scs], excluding any Lvalue Transformation; the identity conversion sequence is considered to be a subsequence of any non-identity conversion sequence) or, if not that,
(emphasis mine)
The important thing to note here is that array to pointer conversion is lvalue transformation.
Upvotes: 1