It's actually quite straight forward. For t[1]
, overload resolution has these candidates:
Candidate 1 (builtin: 13.6/13) (T being some arbitrary object type):
- Parameter list:
(T*, ptrdiff_t)
Candidate 2 (your operator)
- Parameter list:
(TData<float[100][100]>&, something unsigned)
The argument list is given by 13.3.1.2/6
:
The set of candidate functions for overload resolution is the union of the member candidates, the non-member candidates, and the built-in candidates. The argument list contains all of the operands of the operator.
- Argument list:
(TData<float[100][100]>, int)
You see that the first argument matches the first parameter of Candidate 2 exactly. But it needs a user defined conversion for the first parameter of Candidate 1. So for the first parameter, the second candidate wins.
You also see that the outcome of the second position depends. Let's make some assumptions and see what we get:
ptrdiff_t
is int
: The first candidate wins, because it has an exact match, while the second candidate requires an integral conversion.
ptrdiff_t
is long
: Neither candidate wins, because both require an integral conversion.
Now, 13.3.3/1
says
Let ICSi(F) denote the implicit conversion sequence that converts the i-th argument in the list to the type of the i-th parameter of viable function F.
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 ... for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that ...
For our first assumption, we don't get an overall winner, because Candidate 2 wins for the first parameter, and Candidate 1 wins for the second parameter. I call it the criss-cross. For our second assumption, the Candidate 2 wins overall, because neither parameter had a worse conversion, but the first parameter had a better conversion.
For the first assumption, it does not matter that the integral conversion (int to unsigned) in the second parameter is less of an evil than the user defined conversion of the other candidate in the first parameter. In the criss-cross, rules are crude.
That last point might still confuse you, because of all the fuss around, so let's make an example
void f(int, int) { }
void f(long, char) { }
int main() { f(0, 'a'); }
This gives you the same confusing GCC warning (which, I remember, was actually confusing the hell out of me when I first received it some years ago), because 0
converts to long
worse than 'a'
to int
- yet you get an ambiguity, because you are in a criss-cross situation.