Apologies for any faux pas, this is my first post to any forum of this type.
The code below resulted from some experimentation and when run gave output that surprised me, namely
Inside char ch
Inside MyChar ch
My question is why? More precisely, why does the compiler resolve the call to Process by first converting the value returned by MyChar.get to a char and then calling
void Process(char ch)
rather than just calling
void Process(MyChar &ch)
If
MyChar.operator char()
is removed from the definition of MyChar then
void Process(MyChar &ch)
is called as I had expected.
I'm compiling using MS Visual C++ 2008.
This may be a well-known phenomenon, but my search attempts have failed to reveal anything helpful.
Many thanks in advance, Steve.
class MyChar
{
public:
MyChar(){m_ch=1;};
MyChar(char ch){m_ch=ch;};
~MyChar(){};
char m_ch;
operator char(){return m_ch;};
MyChar get(){return *this;};
};
void Process(MyChar &ch);
void Process(char ch);
int main()
{
MyChar Test;
Process(Test.get());
return 0;
}
void Process(MyChar &ch)
{
printf("\nInside MyChar ch");
}
void Process(char ch)
{
printf("\nInside char ch");
MyChar chch(ch);
Process(chch);
}
VS 2008 is really old by now and not exactly known for its stellar standards compliance. In general, expect some weird behaviour. See the comment by @IgorTandetnik: contrary to the C++ language rules VS 2008 sometimes allows you to bind a temporary object to a non-const lvalue reference.
Here’s a breakdown of what’s going on:
Test.get()
returns a copy of the Test
object. It’s a temporary because you pass it directly to Process()
(in a nutshell, if it doesn’t have a name, it’s a temporary). Process(MyChar&)
would be the closest match regarding type, but it is not suitable because only a const lvalue reference can bind to a temporary. But MyChar
has an implicit conversion to char, so the next best overload is Process(char)
, which is picked.Process()
call triggers another overload resolution. You call it with the MyChar
lvalue chch
. A MyChar&
can bind to a MyChar
lvalue no problem. Because of the identical types Process(MyChar&)
is the best match and gets picked. Process(char)
is a candidate too, but because it involves a type conversion, it is a worse match.So far, so good. VS 2008 behaves exactly as expected and mandated by the standard. When you now remove the implicit conversion, VS 2008’s non-standard behaviour kicks in. Process(char)
isn’t a valid function to call any more, so only Process(MyChar&)
remains as a candidate and is picked.
If you’re concerned about cross-compiler compatibility at all, you must not rely on this, though. For example, with GCC 7.3 the no-conversion-operator version of your code will fail to compile with:
error: cannot bind non-const lvalue reference of type ‘MyChar&’ to an rvalue of type ‘MyChar’
Clang gives a similar error message. The standard conforming way to achieve your desired behaviour is either to add const to the Process
function’s signature:
Process(const MyChar&)
or to not pass a temporary:
MyChar Test;
MyChar AnotherChar = Test.get();
Process(AnotherChar);