I've a (member-)fuction overloaded like this:
bool foo(bool);
int foo(int);
float foo(float);
...
std::string foo( std::string const&);
for a couple of build-in-types but not for const char*. Calling foo("beauty is only skin-deep");, to my big suprise, called the bool-variant of the foo function. This leads to my questions:
QUESTION: Is there a well defined implicit conversion order for build-in-types
NOT THE QUESTION: How to avoid implicit conversion. How evil is implicit conversion. ...
EDIT: removed question about implicit converstion order for user-defined-questions
according to: http://en.cppreference.com/w/cpp/language/implicit_cast
all build-in conversions take place before user defined ones
pointer -> bool is a 'Boolean conversions' (needed for if(pointer) notation), last of 'numeric conversions'
'const char*' -> std::string is a 'user defined conversion' as from language point of view, std::string is a user defined type.
Unfortunately simplest solution is to write proper overload of fun(const char*), or to avoid fun(bool) vs fun(std::string) overloads
Related
You can find the text below in Appendix B of the book "C++ Templates The Complete Guide" by David Vandevoorde and Nicolai Josuttis.
B.2 Simplified Overload Resolution
Given this first principle, we are left with specifying how well a
given argument matches the corresponding parameter of a viable
candidate. As a first approximation we can rank the possible matches
as follows (from best to worst):
Perfect match. The parameter has the type of the expression, or it has a type that is a reference to the type of the expression (possibly
with added const and/or volatile qualifiers).
Match with minor adjustments. This includes, for example, the decay of an array variable to a pointer to its first element, or the
addition of const to match an argument of type int** to a parameter of
type int const* const*.
Match with promotion. Promotion is a kind of implicit conversion that includes the conversion of small integral types (such as bool,
char, short, and sometimes enumerations) to int, unsigned int, long or
unsigned long, and the conversion of float to double.
Match with standard conversions only. This includes any sort of standard conversion (such as int to float) but excludes the implicit
call to a conversion operator or a converting constructor.
Match with user-defined conversions. This allows any kind of implicit conversion.
Match with ellipsis. An ellipsis parameter can match almost any type (but non-POD class types result in undefined behavior).
A few pages later the book shows the following example and text (emphasis mine):
class BadString {
public:
BadString(char const*);
...
// character access through subscripting:
char& operator[] (size_t); // (1)
char const& operator[] (size_t) const;
// implicit conversion to null-terminated byte string:
operator char* (); // (2)
operator char const* ();
...
};
int main()
{
BadString str("correkt");
str[5] = 'c'; // possibly an overload resolution ambiguity!
}
At first, nothing seems ambiguous about the expression str[5]. The
subscript operator at (1) seems like a perfect match. However, it is
not quite perfect because the argument 5 has type int, and the
operator expects an unsigned integer type (size_t and std::size_t
usually have type unsigned int or unsigned long, but never type int).
Still, a simple standard integer conversion makes (1) easily viable.
However, there is another viable candidate: the built-in subscript
operator. Indeed, if we apply the implicit conversion operator to str
(which is the implicit member function argument), we obtain a pointer
type, and now the built-in subscript operator applies. This built-in
operator takes an argument of type ptrdiff_t, which on many platforms
is equivalent to int and therefore is a perfect match for the argument
5. So even though the built-in subscript operator is a poor match (by user-defined conversion) for the implied argument, it is a better
match than the operator defined at (1) for the actual subscript! Hence
the potential ambiguity.
Note that the first list is Simplified Overload Resolution.
To remove the "possibly" and "potential" about whether or not int is the same as ptrdiff_t, let's change one line:
str[(ptrdiff_t)5] = 'c'; // definitely an overload resolution ambiguity!
Now the message I get from g++ is:
warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: [enabled by default]
and --pedantic-errors promotes that warning to an error.
So without diving into the standard, this tells you that the simplified list is only part of the story. That list tells you which of several possible routes to prefer when going from A to B, but it does not tell you whether to prefer a trip from A to B over a trip from C to D.
You can see the same phenomenon (and the same g++ message) more obviously here:
struct S {
explicit S(int) {}
operator int() { return 0; }
};
void foo(const S&, long) { }
void foo(int, int) { }
int main() {
S s(0);
foo(s, 1);
}
Again the call to foo is ambiguous, because when we have a choice of which argument to implicitly convert in order to choose an overload, the rules don't say, "pick the more lightweight conversion of the two and convert the argument requiring that conversion".
Now you're going to ask me for the standard citation, but I shall use the fact I need to hit the hay as an excuse for not looking it up ;-)
In summary it is not true here that "a user defined conversion is a better match than a standard integer conversion". However in this case the two possible overloads are defined by the standard to be equally good and hence the call is ambiguous.
str.operator[](size_t(5));
Write code in definite way and you'll not need to bother about all this stuff :)
There is much more to thing about.
Question was changed!
I use a simple way to hide my enums from local namespaces - enumeration inside of a struct. It goes roughly like this:
struct Color
{
enum Type
{
Red, Green, Black
};
Type t_;
Color(Type t) : t_(t) {}
operator Type () const {return t_;}
private:
template<typename T>
operator T () const;
};
operator T () is a protection from implicit type casting. Then I tried to compile this code with gcc and with keil:
Color n;
int a[9];
a[ (int)n ] = 1;
gcc compiled it with no error (wich is what I expected), but Keil gived me an error:
"invalid type conversion. operator () is inaccessible".
So my question is: which compiler is right?
I know about c++11 enum class, but it isn't supported by Keil now
Should reinterpret_cast (not c-style () cast) call type conversion operator?
No, reinterpret_cast is only used for a few dodgy types of conversions:
converting pointers to integers and back
converting between pointers (and references) to unrelated types
You shouldn't need a cast at all to use the implicit conversion operator - you have not prevented implicit conversion at all. In C++11, if the operator were explicit, then you'd need a static_cast.
If you're stuck with C++03, and you really want to prevent implicit conversion but allow explicit conversion, then I think the only sensible thing to do is to provide a named conversion function.
Update: The question has now changed, and is asking about C-style casting rather than reinterpret_cast. That should compile since any conversion that can be done by static_cast (including implicit conversions) can also be done with a C-style cast.
I recently came across a class like the following
class Foo {
public:
Foo(std::string msg) {}
private:
Foo(bool b) {}
};
I noticed that trying to create an object of this class by means of
Foo foo("blah");
causes a compilation error telling that Foo::Foo(bool) is private. Apparently, if the argument is not an actual std::string, the compiler prefers to use the constructor with the bool argument. On the other hand, if the private constructor is not given, above code compiles just fine.
Why is it that the "bool-constructor" has precedence over the "string-constructor" althogh the type of the passed argument does not fit to any of them? Is this just a matter of definition or does it have a deeper meaning and some good reason?
The reason has to do with conversion operator precedence. Each of the calls includes an implicit conversion
pointer -> std::string
pointer -> bool
In this case #1 is a user defined conversion and #2 is a language / compiler defined one. User defined conversions have a much lower precedence and hence the other conversion is preferred.
Edit
Here is a similar question which has a much more in depth explanation of the precedence checks involved
conversion precedence in c++
When I try to compile (with gcc 4.3.4) this code snippet:
enum SimpleEnum {
ONEVALUE
};
void myFunc(int a) {
}
void myFunc(char ch) {
}
struct MyClass {
operator int() const { return 0; };
operator SimpleEnum() const { return ONEVALUE; };
};
int main(int argc, char* argv[]) {
myFunc(MyClass());
}
I get this error:
test.cc: In function "int main(int, char**)":
test.cc:17: error: call of overloaded "myFunc(MyClass)" is ambiguous
test.cc:5: note: candidates are: void myFunc(int)
test.cc:8: note: void myFunc(char)
I think I (almost) understand what the problem is, i.e. (simplifying it a lot) even if I speak about "char" and "enum", they all are integers and then the overloading is ambiguous.
Anyway, the thing I don't really understand is that if I remove the second overloading of myFunc OR one of the conversion operators of MyClass, I have no compilation errors.
Since I'm going to change A LOT of old code because of this problem (I'm porting code from an old version of HP-UX aCC to g++ 4.3.4 under Linux), I would like to understand better the whole thing in order to choose the best way to modify the code.
Thank you in advance for any help.
The conversion from MyClass is ambiguous, as there is one conversion to int and one to the enum, which is itself implicitly convertible to int, and both are equally good conversions. You can just make the call explicit, though, by specifying which conversion you want:
myfunc(int(MyClass()));
Alternatively, you might like to rethink why you have a function that has separate overloads for int and char, perhaps that could be redesigned as well.
enums are types in C++, unlike C.
There are implicit conversions for both enum -> char and enum -> int. The compiler just doesn't know which one to choose.
EDIT: After trying with different tests:
When the definition for custom conversion MyClass -> int is removed, code compiles.
Here there is implicit conversion for enum to int and so it is the one favored by the compiler over the one to char. Test here.
When the definition for void myFunc(int) is removed compilation fails.
Compiler tries to convert from MyClass to char and finds that, not having a user defined conversion operator char(), both user defined int() and SimpleEnum() may be used. Test here.
When you add a char() conversion operator for MyClass compilation fails with the same error as if not.
Test here.
So the conclusion I come up with here is that in your originally posted code compiler has to decide which of the two overloaded versions of myFunc should be called.
Since both conversions are possible:
MyClass to int via user defined conversion operator.
MyClass to int via user defined conversion (MyClass to SimpleEnum) + implicit conversion (SimpleEnum to char)
compiler knows not which one to use.
I would have expected that the int overload is called. Tried a few compilers and got different results. If you are going to remove anything, remove the user conversion operator to int, since enums have a standard conversion to ints.
I saw the following code:
class NullClass {
public:
template<class T> operator T*() const { return 0; }
};
const NullClass NULL;
void f(int x);
void f(string *p);
f(NULL); // converts NULL to string*, then calls f(string*)
Q1> I have problems to understand the following statement
template<class T> operator T*() const { return 0; }
Specially, what is the meaning of operator T*()?
Q2> Why f(NULL) is finally triggering the f(string*)?
Thank you
what is the meaning of operator T*()?
It is a user-defined conversion operator. It allows an object of type NullClass to be converted to any pointer type.
Such conversion operators can often lead to subtle, unexpected, and pernicious problems, so they are best avoided in most cases (they are, of course, occasionally useful).
Why f(NULL) is finally triggering the f(string*)?
NULL is of type NullClass. It can't be converted to an int, but the user-defined conversion NullClass -> T* can be used to convert it to a string*, so void f(string*) is selected.
Note that this works because there is only one overload of f that takes a pointer. If you had two overloads,
void f(int*);
void f(float*);
the call would be ambiguous because the NullClass -> T* conversion can be converted to both int* and float*.
template<class T> operator T*() means that there is an implicit conversion from NullClass to T* for any T.
When you're calling f(NULL), the compiler needs to decide which overload to use. Since neither overload takes an object of type NullClass, it looks which implicit conversions exist. There is no conversion to int, but there is one to string*, so the conversion is applied and the string* overload is called.
operator Anything() overloads the "cast" operator. Whenever NullClass needs to be converted to Anything, this function will be called, and the result will be used.
In your case, Anything is T* where T can be any type (it is a template parameter), meaning a NullClass supports casting to any pointers.
Since NullClass can be cast into any pointers, including a string*. So the f(string*) version will be used.
Q1> I have problems to understand the following statement
template<class T> operator T*() const { return 0; }
Specially, what is the meaning of operator T*()?
It's an implicit conversion operator. It makes it possible to have objects of the type it belongs to implicit convert to the target type T* here. This version is a special one, since, being a template, it can convert an object of that NullClass to any pointer type.
Implicit conversion are frowned upon for good reasons. They have the bad habit of kicking in at unexpected moments, making the compiler call an unintended version of an overloaded function. Having a templatized implicit conversion operator is especially evil because templatization multiplies the possibilities.
Q2> Why f(NULL) is finally triggering the f(string*)?
See above. There is no possible conversion to int, so the implicit conversion operator kicks in and converts an NullClass object into any pointer requested.
I suppose this is intended. Usually you don't want a pointer to be converted to an integer, which is why that class has an implicit conversion into any pointer, but none to int.
That NullClass isn't all that bad, but the NULL instance of it is pure stupidity. As soon as you include any of the many headers that define the NULL macro (defined to be 0, i.e. an integer constant), the preprocessor will trample over all source and replace each usage of NULL with 0. Since you can't avoid to include this, this error renders the whole class pretty much useless.
The NuLLClass provides a conversion function to convert to a pointer. When you call f(NULL), it will try to find a way to convert NULL to a valid argument for f.
Because you can call operator T*() on NULL, it will with T=string. This fulfills the demand for f(string *). Since there is no way to convert NULL to an int, there is only one clear choice of which function to call.
Specially, what is the meaning of operator T*()?
It is a conversion operator to type T*. It allows operations such as (T*)NULL.
Q2> Why f(NULL) is finally triggering the f(string*)?
Because the compiler looks for the best match between the arguments and the method's signature, in this case choosing template<typename T> NullClass::operator T*(), where T=string.