I wrote a little C++ wrapper around some parts of GSL and encounter the following puzzle (for me). The code (reduced to its essentials) is as follows:
#include <stdlib.h>
struct gsl_vector_view {};
class Vector : protected gsl_vector_view {
public:
Vector ( const Vector& original );
Vector ( const gsl_vector_view view );
};
class AutoVector : public Vector {
public:
explicit AutoVector ( const size_t dims );
};
void useVector ( const Vector b ) {}
void test () {
const AutoVector ov( 2 );
useVector( ov );
}
will not compile using gcc 4.4.5 g++ -c v.cpp but yield
In function ‘void test()’:
19: error: call of overloaded ‘Vector(const AutoVector&)’ is ambiguous
7: note: candidates are: Vector::Vector(gsl_vector_view)
6: note: Vector::Vector(const Vector&)
19: error: initializing argument 1 of ‘void useVector(Vector)’
I am surprised that the protected base class gsl_vector_view is taken into consideration by the call of useVector( Vector ). I would have thought that useVector belongs to "the general public" in the parlance of "The C++ Programming Language", 3rd e., p. 405 and thus does not have access to that protected information and, hence, cannot be confused by it. I know that I can get rid of the ambiguity by declaring the constructor as
explicit Vector ( const gsl_vector_view view );
What I did not know (and, honestly, do not understand either), is that the ambiguity of the overloaded call disappears when I declare the constructor as
Vector ( const gsl_vector_view& view );
i.e. pass the argument by reference (which I would anyway consider the proper way of doing things).
There are two ways to resolve this ambiguity: Typecast char to float. Remove either one of the ambiguity generating functions float or double and add overloaded function with an int type parameter.
If the compiler can not choose a function amongst two or more overloaded functions, the situation is -” Ambiguity in Function Overloading”. The reason behind the ambiguity in above code is that the floating literals 3.5 and 5.6 are actually treated as double by the compiler.
Access to a base class member is ambiguous if you use a name or qualified name that does not refer to a unique function or object. The declaration of a member with an ambiguous name in a derived class is not an error. The ambiguity is only flagged as an error if you use the ambiguous member name.
Overload resolution is done before access checking, that is why even the protected base class' members are considered.
Overload resolution is described in chapter 13.3 of the standard. My interpretation is that binding const AutoVector ov
to Vector ( const Vector& original );
is user defined conversion, a derived-to-base Conversion ([13.3.3.1.4/1]) kind. For Vector ( const gsl_vector_view view );
, the conversion sequence is also user defined conversion because it is lvalue-to-rvalue conversion followed by user defined conversion. So, both conversion sequences are considered equal, none is better than the other, and thus you get the ambiguity.
Now, if you change the ctor to Vector ( const gsl_vector_view& view );
, both conversion are lvalue-to-value conversion followed by user defined conversion (derived-to-base Conversion). Those two can be ordered ([13.3.3.2/4]) and the conversion to const Vector&
is considered better and thus there is no ambiguity.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With