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.
Related
Why is the following allowed to be compiled in C++?
#include<iostream>
using namespace std;
class mytest
{
public:
operator int()
{
return 10;
}
operator const int()
{
return 5;
}
};
int main()
{
mytest mt;
//int x = mt; //ERROR ambigious
//const int x = mt; //ERROR ambigious
}
Why does it make sense to allow different versions (based on constness) of the conversion operator to be compiled when their use always results in ambiguity?
Can someone clarify what I am missing here?
For conversion they're ambiguous; but you might call them explicitly. e.g.
int x = mt.operator int();
const int x = mt.operator const int();
I believe that in the strictest sense, even if it doesn't really make much sense for const, this is legitimate.
There is a difference between a function declaration and a function type, and they do not have the same constraints.
Function declarations may not differ in only their return type or (since C++17) exception specification. However, no such thing is said about the function type (to my knowledge).
The standard [class.conv.fct] decribes conversion functions as having such-and-such form (three alternatives listed), all of which do not look like normal function declarations, in particular they very obviously have no return type.
It does state, that the function type is "function taking no parameter returning conversion-type-id", but nowhere is it mentioned that conversion function declarations have any such thing as a return type. On the contrary, the three alternative forms listed very clearly do not have a return type.
Since conversion functions don't have a return type (... in their declaration), it cannot conflict. So, I guess, in the strictest, most pedantic sense, it's even kinda "legal", whether it makes sense or not.
If you think about it, then it somehow has to be legal, too. A class may very well have more than one conversion function to different things (not just differing by const). Such code does exist, and it sometimes makes a lot of sense going that way.
You might for example have a class File that converts to either a string (the filename) or to a handle_t (the operating system handle) in case you want to use some OS-specific or exotic function that your wrapper class doesn't directly support (think writev, tee, or epoll?) It's certainly something you'd expect to work!
If, however, we treated conversion functions as "just functions", then they'd only differ in their return type, which would render the declarations illegal. So... that wouldn't work.
why does it make sense to allow different version(based on constness) of conversion operator (to be compiled) when their use always result in ambiguity;
It usually makes no sense (apart from highly artificial use cases) and a compiler could warn you about it:
prog.cc:12:25: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
operator const int()
^
I come to the conclusion, that it's not explicitly allowed to write conversation operators that only differ by constness of their return value. It is too expensive for the compilation process to explicitly disallow it.
Remember that (member) functions that only differ by their return type
class mytest
{
int f();
const int f();
};
are forbidden:
error: ‘const int mytest::f()’ cannot be overloaded
It's just that conversion operators start with operator that makes the difference.
Consider the following code:
class DictionaryRef {
public:
operator bool() const;
std::string const& operator[](char const* name) const;
// other stuff
};
int main() {
DictionaryRef dict;
char text[256] = "Hello World!";
std::cout << dict[text] << std::endl;
}
This produces the following warning when compiled with G++:
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:
note: candidate 1: const string& DictionaryRef::operator[](const char*) const
note: candidate 2: operator[](long int, char*) <built-in>
I know what this means (and the cause is been explained in operator[](const char *) ambiguity) but I'm looking for a way to ensure correct behavior/resolve the warning without changing my class design - since it makes perfect sense for a class to have both a boolean conversion, and a [](const char*) operator.
What's the purpose of operator[](long int, char*) besides generating random compiler warnings? I can't imagine someone writing 1["hello"] in real code.
Although you "can't imagine someone writing 1["hello"] in a real code", this is something that's legal C++, as a consequence of the commutative [] inherited from C. Reasonable or not, that's how the language is defined, and it's unlikely to be changing for us.
The best way to avoid the ambiguity is to add explicit to the boolean conversion - it's very rare that we ever want a non-explicit operator bool().
An alternative is to replace operator bool() with an operator void*(), which will still satisfy boolean tests, but not convert to integer.
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
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++
I have seen other questions on SO regarding this, but none that explains it in full. What is the right ways for compilers to handle the two situations below? I have tried it with gcc 4.7.1 (with -std=c++0x), VS2010 and VS2012 an get different results on all:
Example 1:
struct BB
{
// generic cast
template<typename T>
operator T() const
{
return 0;
}
// string cast
operator std::string() const
{
return string("hello");
}
};
int main()
{
BB b;
string s = b;
}
Output:
gcc 4.7.1: Ok
VS2010: Ok
VS2012: Fail: "Cannot convert from BB to
string"
Example 2:
struct BB
{
// generic cast
template<typename T>
operator T() const
{
return 0;
}
// string cast
operator std::string() const
{
return string("hello");
}
};
int main()
{
BB b;
string s = (string)b;
Output:
gcc 4.7.1: Fail: call of overloaded string(BB&) is ambigious
VS2010: Ok
VS2012: Fail: "Cannot convert from BB to string"
Your second version with a C-style cast is ambiguous. The problem is that there are two ways that static_cast<std::string> can convert an instance of class BB to a string. The obvious path is by using your non-template std::string cast operator to convert directly to a string. There is a more devious path, and all but VS2010 have found it. This other path is to use your template cast operator to convert a BB to an allocator for a string and then construct an empty string using this allocator.
Template cast operators are potentially dangerous beasts. You have just given the compiler the tools to convert a BB to anything.
Your first version escapes this problem because std::basic_string(const _Alloc& __a) is an explicit constructor. Explicit constructors can be used by explicit conversions but not by implicit ones. It is this constructor plus the conversion to an allocator that creates the ambiguity, and this path cannot be used with an implicit conversion.
As to why VS1012 fails on the implied conversion, it could be a bug in VS2012, or it could be that C++11 creates even more avenues to get from a BB to a std::string. I am not a C++11 expert. I'm not even a novice.
One more item: your code would have failed even more miserably had you used
std::string s;
s = b;
The assignment operator in conjunction with the template conversion operator creates more ways to get from b to s. Should the system convert b to a std::string, to a char, or a char*?
Bottom line: You really should rethink your use of template conversion operators.
The reason is fails under some compilers is because they go to different lengths trying to figure out what you're doing. The culprit is the templated operator call.
As this SO question explains, templated operators have to be called explicitly (b.operator()<std::string> if you wanted to call template<typename T> operator();), but, there is no way to call your templated operator:
b.operator std::string()<std::string>; //invalid
b.operator std::string <std::string>(); //also invalid, string takes no template arguments
// a bunch more weird syntax constructions...
The issue comes from the fact that the name after operator depends on the template argument, so there is no way to specify it. gcc and VS2012 figured out what you were doing and noticed they can fit the conversion to either the template or the explicitly defined one => ambiguous call. VS2010 didn't do so and is calling one of them, you can find which one through debugging.
Template specialization could've helped in a case like this, however, trying to define
// string cast
template<>
operator std::string() const
{
return string("hello");
}
will fail, since the compiler can't tell the difference between that function and a regular one without the template<>. If there were some arguments in the prototype it would've worked, but operator typename does have any...
Long story short - avoid templated conversion operators.