This question is related to overloading in JavaScript. Once you get to
known with it -- you understand reason of "weird behavior" of your
code.
As far as I understand, the JS does not have overload support defined in the ECMA specification. What is suggested in various places are JS hacks to mock overload support.
The correct answer about behavior in Qt is as follows:
The order dependent behavior is documented only in the source code, as comment. See qv4qobjectwrapper.cpp in qtdeclarative module.
Resolve the overloaded method to call. The algorithm works conceptually like this:
1. Resolve the set of overloads it is *possible* to call.
Impossible overloads include those that have too many parameters or have parameters
of unknown type.
2. Filter the set of overloads to only contain those with the closest number of
parameters.
For example, if we are called with 3 parameters and there are 2 overloads that
take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
3. Find the best remaining overload based on its match score.
If two or more overloads have the same match score, call the last one. The match
score is constructed by adding the matchScore() result for each of the parameters.
The implementation:
static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache,
QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
{
int argumentCount = callArgs->argc();
QQmlPropertyData best;
int bestParameterScore = INT_MAX;
int bestMatchScore = INT_MAX;
QQmlPropertyData dummy;
const QQmlPropertyData *attempt = &data;
QV4::Scope scope(engine);
QV4::ScopedValue v(scope);
do {
QQmlMetaObject::ArgTypeStorage storage;
int methodArgumentCount = 0;
int *methodArgTypes = nullptr;
if (attempt->hasArguments()) {
int *args = object.methodParameterTypes(attempt->coreIndex(), &storage, nullptr);
if (!args) // Must be an unknown argument
continue;
methodArgumentCount = args[0];
methodArgTypes = args + 1;
}
if (methodArgumentCount > argumentCount)
continue; // We don't have sufficient arguments to call this method
int methodParameterScore = argumentCount - methodArgumentCount;
if (methodParameterScore > bestParameterScore)
continue; // We already have a better option
int methodMatchScore = 0;
for (int ii = 0; ii < methodArgumentCount; ++ii) {
methodMatchScore += MatchScore((v = QV4::Value::fromStaticValue(callArgs->args[ii])),
methodArgTypes[ii]);
}
if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
best = *attempt;
bestParameterScore = methodParameterScore;
bestMatchScore = methodMatchScore;
}
if (bestParameterScore == 0 && bestMatchScore == 0)
break; // We can't get better than that
} while ((attempt = RelatedMethod(object, attempt, dummy, propertyCache)) != nullptr);
if (best.isValid()) {
return CallPrecise(object, best, engine, callArgs, callType);
} else {
QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
const QQmlPropertyData *candidate = &data;
while (candidate) {
error += QLatin1String("\n ") +
QString::fromUtf8(object.metaObject()->method(candidate->coreIndex())
.methodSignature());
candidate = RelatedMethod(object, candidate, dummy, propertyCache);
}
return engine->throwError(error);
}
}
I found overload support being mentioned in Qt 4.8 documentation (https://doc.qt.io/archives/qt-4.8/qtbinding.html). It does not go into any details:
QML supports the calling of overloaded C++ functions. If there are
multiple C++ functions with the same name but different arguments, the
correct function will be called according to the number and the types
of arguments that are provided.
Note that Qt 4.x series are really old and archived. I see that the same comment exists in Qt 5.x source code as well:
src/qml/doc/src/cppintegration/exposecppattributes.qdoc:QML supports the calling of overloaded C++ functions. If there are multiple C++
I agree that it is really strange to have this order dependent logic "by design". Why would that be desired behavior? If you read the source code of that function very carefully, there are more surprises in there (at least initially). When you provide too little arguments at the call site, you get an error message listing available overloads, when you provide too many arguments, the extra arguments are silently ignored. Example:
Q_INVOKABLE void fooBar(int) {
qDebug() << "fooBar(int)";
};
Q_INVOKABLE void fooBar(int, int) {
qDebug() << "fooBar(int, int)";
};
Qml call site.
aPieChart.fooBar();
The error message.
./chapter2-methods
qrc:/app.qml:72: Error: Unable to determine callable overload. Candidates are:
fooBar(int,int)
fooBar(int)
Qml call site.
aPieChart.fooBar(1,1,1);
The run-time output (the last arg is ignored as the two arg overload is selected) :
fooBar(int, int)
According to What happens if I call a JS method with more parameters than it is defined to accept? passing too many args is a valid syntax.
It is also worth noting that default parameter values are supported by the overloading mechanism. Details are as follows:
Implementation in Qt Quick relies on Qt Meta Object system to enumerate
the function overloads. Meta object compiler creates one overload for each
argument with default values. For example:
void doSomething(int a = 1, int b = 2);
becomes (CallOverloaded() will consider all three of these) :
void doSomething();
void doSomething(a);
void doSomething(a, b);