Reputation: 117
I'm having a weird problem with an overloaded function one of my work colleagues made, but we're both C++/QT newbies and can't figure out the reason for such a situation.
The situation is as follows:
We have an overloaded function:
foo(bool arg1 = false);
foo(const QVariant &arg1, const QString &arg2 = NULL, bool arg3 = false);
On the first one we have only one optional bool argument passed as value; On the second we have one QVariant reference, an optional qstring reference and a bool.
What happens on runtime is the when I call for example:
foo("some_c++_string");
instead of using the 2nd one and casting the string to QVariant, it uses the first one and probably ignores the argument! The weird thing is that it doesn't even complain about not existing an overload for foo(char *) on compile time for example! But if we do:
foo(QString("some_qt_string"));
it goes to the second overload as expected.
So, the question is: Why in the world does it decide to go for an overload which accepts one optional argument not even of the same type instead of using the second one and trying to use the string argument as a QVariant?
It probably has something to do with passing the arguments as reference and giving them default values, but I've tried several combinations and always went wrong.
Thanks for your time
Upvotes: 3
Views: 805
Reputation: 171127
That's because of the ordering of implicit conversions. Converting a string literal to a bool
requires only a standard conversion sequence: an array-to-pointer conversion (obtaining const char*
) and a boolean conversion (obtaining bool
).
On the other hand, converting a string literal to a QVariant
requires a user-defined conversion sequence, since it involves a class (QVariant
).
And per C++11 13.3.3.2/2,
When comparing the basic forms of implicit conversion sequences (as defined in 13.3.3.1)
- a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence
Which means that the first overload is strictly better and is thus chosen.
I'd wager you're building with Visual Studio, by the way. Otherwise, the construct
foo(QString("some_qt_string"));
wouldn't even compile. That's because QString("some_qt_string")
creates a temporary object, and in standard C++, temporaries cannot bind to non-const lvalue references (which your second overload takes). However, Visual Studio allows that as an extension.
Upvotes: 4
Reputation: 64223
Why in the world does it decide to go for an overload which accepts one optional argument not even of the same type instead of using the second one and trying to use the string argument as a QVariant?
Looking at the functions signature :
foo(bool arg1 = false); foo(QVariant &arg1, QString &arg2 = NULL, bool arg3 = false);
The 2nd overload takes an lvalue. If you pass a const char*
, it would require creating a QVariant rvalue.
On the other hand, there is a function overload that takes a bool value, which is a better match, and is called.
You also said that if you do this :
foo(QString("some_qt_string"));
the second is called. But it shouldn't even compile. You are passing rvalue to a function which takes a lvalue. I am not sure which compiler you use, but that is certainly wrong. Put maximum possible warning level and see what happens. I do hope you are not ignoring compiler warnings ;)
The simplest solution (and maybe the best) is NOT to overload the function. Or at least not to give default arguments.
Upvotes: 3
Reputation: 1243
The second overload takes a QVariant
by reference as it's first argument. You did not pass a QVariant
, so this overload can never be taken. In fact, when you pass a QString
as first argument, the second overload is still not viable and you should get a compiler error. My guess is that you are compiling with Microsoft VC and what you are observing is non-standard behavior that allows you to take an l-value reference of a temporary.
Upvotes: 2