Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this ambiguity here?

Consider I have the following minimal code:

#include <boost/type_traits.hpp>  template<typename ptr_t> struct TData {     typedef typename boost::remove_extent<ptr_t>::type value_type;     ptr_t data;      value_type & operator [] ( size_t id ) { return data[id]; }     operator ptr_t & () { return data; } };  int main( int argc, char ** argv ) {     TData<float[100][100]> t;        t[1][1] = 5;     return 0; } 

GNU C++ gives me the error:

test.cpp: In function 'int main(int, char**)': test.cpp:16: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for second: test.cpp:9: note: candidate 1: typename boost::remove_extent<ptr_t>::type& TData<ptr_t>::operator[](size_t) [with ptr_t = float [100][100]] test.cpp:16: note: candidate 2: operator[](float (*)[100], int) <built-in> 

My questions are:

  1. Why GNU C++ gives the error, but Intel C++ compiler is not?
  2. Why changing operator[] to the following leads to compiling without errors?

    value_type & operator [] ( int id ) { return data[id]; }

Links to the C++ Standard are appreciated.


As I can see here are two conversion paths:

  1. (1)int to size_t and (2)operator[](size_t).
  2. (1)operator ptr_t&(), (2)int to size_t and (3)build-in operator[](size_t).
like image 295
Kirill V. Lyadvinsky Avatar asked Aug 19 '10 06:08

Kirill V. Lyadvinsky


People also ask

What are reasons for ambiguity?

Phonetics, grammar, semantics, syntax, as small as punctuation and intonation can all be the cause of ambiguity. Based on this, linguists divide ambiguity into different types such as phonetic ambiguity, lexical ambiguity, syntactic ambiguity, and pragmatic ambiguity.

What do u mean by ambiguity?

Definition of ambiguity 1a : the quality or state of being ambiguous especially in meaning The ambiguity of the poem allows several interpretations. b : a word or expression that can be understood in two or more possible ways : an ambiguous word or expression. 2 : uncertainty.

What are some examples of ambiguity?

Examples and Observations"I can't tell you how much I enjoyed meeting your husband." "We saw her duck is a paraphrase of We saw her lower her head and of We saw the duck belonging to her, and these last two sentences are not paraphrases of each other. Therefore We saw her duck is ambiguous."


2 Answers

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:

  1. ptrdiff_t is int: The first candidate wins, because it has an exact match, while the second candidate requires an integral conversion.
  2. 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.

like image 172
Johannes Schaub - litb Avatar answered Oct 02 '22 16:10

Johannes Schaub - litb


With the expression:

t[1][1] = 5; 

The compiler must focus on the left hand side to determine what goes there, so the = 5; is ignored until the lhs is resolved. Leaving us with the expression: t[1][1], which represents two operations, with the second one operating on the result from the first one, so the compiler must only take into account the first part of the expression: t[1].The actual type is (TData&)[(int)]

The call does not match exactly any functions, as operator[] for TData is defined as taking a size_t argument, so to be able to use it the compiler would have to convert 1 from int to size_t with an implicit conversion. That is the first choice. Now, another possible path is applying user defined conversion to convert TData<float[100][100]> into float[100][100].

The int to size_t conversion is an integral conversion and is ranked as Conversion in Table 9 of the standard, as is the user defined conversion from TData<float[100][100]> to float[100][100] conversion according to §13.3.3.1.2/4. The conversion from float [100][100]& to float (*)[100] is ranked as Exact Match in Table 9. The compiler is not allowed to choose from those two conversion sequences.

Q1: Not all compilers adhere to the standard in the same way. It is quite common to find out that in some specific cases a compiler will perform differently than the others. In this case, the g++ implementors decided to whine about the standard not allowing the compiler to choose, while the Intel implementors probably just silently applied their preferred conversion.

Q2: When you change the signature of the user defined operator[], the argument matches exactly the passed in type. t[1] is a perfect match for t.operator[](1) with no conversions whatsoever, so the compiler must follow that path.

like image 27
David Rodríguez - dribeas Avatar answered Oct 02 '22 15:10

David Rodríguez - dribeas