C++ 模板函数调用似乎更喜欢不太专业的函数调用
C++ template function call seems to prefer the less specialized one
我试图通过使用 C++ 模板在一定程度上减少样板代码来简化我早期的一些 JSON 序列化。一切顺利,直到我想序列化列表等类型,因为它们本身也是模板,并且似乎需要部分模板专门化,而模板函数似乎不存在。
因此,我应用了在这里找到的一个巧妙的小技巧:
https://www.fluentcpp.com/2017/08/15/function-templates-partial-specialization-cpp/
namespace Support {
// These are in header files
template <typename T>
struct convertType{};
// Specialised template that serialises a list by iterating over its members
template <typename T>
QJsonValue toJsonValue(const QList<T> &source, convertType<QList<T>>) {
QJsonArray result;
for (auto it = source.cbegin(); it != source.cend(); it++) {
result.push_back(*it);
}
return result;
}
// "Fallback" template that generates an assertion
template <typename T>
QJsonValue toJsonValue(const T &source, convertType<T>) {
// This function should never be called.
std::string msg = "toJsonValue called with unimplemented type ";
msg += typeid (T).name();
Q_ASSERT_X(false, "toJsonValue<T>", msg.c_str()); // Always asserts.
return QJsonValue();
}
// Convenience function
template<typename T>
QJsonValue toJsonValue(const T &source) {
return toJsonValue<T>(source, convertType<T>{});
}
// This one is in the CPP file
// Integer
template <>
QJsonValue toJsonValue(const int &source, convertType<int>) {
return QJsonValue(source);
}
} // NS Support
当我用整数调用这个模板时如下:
qDebug() << Support::toJsonValue<int>(3713); // if not familiar with Qt, think as qDebug() being the same as std::cout.
如预期的那样输出“QJsonValue(double, 3713)”。
但是,当我尝试按如下方式传递列表时:
QList<int> foo = {1, 2, 3};
qDebug() << Support::toJsonValue<QList<int>>(foo);
在我看来,代码将采用最不专业的函数模板,即生成断言的函数模板。我不知道为什么会发生这种情况,因为我希望它会采用专门用于 QList
.
的函数模板
有人知道为什么会这样吗?我可能滥用了模板吗?
这两个是等价的吗?看起来你创建了一个重载而不是部分专业化。
// Integer
template <>
QJsonValue toJsonValue(const int &source, convertType<int>) {
return QJsonValue(source);
}
// Integer
template <>
QJsonValue<int> toJsonValue(const int &source, convertType<int>) {
return QJsonValue(source);
}
I have no idea why on earth this is happening, as I would expect that it would take the function template specialised for the QList.
Does anyone have an idea why this is happening?
当你打电话时
Support::toJsonValue<QList<int>>(foo);
以下“便捷函数”匹配
template<typename T>
QJsonValue toJsonValue(const T &source) {
return toJsonValue<T>(source, convertType<T>{});
}
和 T
是 QList<int>
。
所以内部调用
toJsonValue<T>(source, convertType<T>{})
成为
toJsonValue<QList<int>>(source, convertType<QList<int>>{});
// ........^^^^^^^^^^^^
// ........^^^^^^^^^^^^ here is the problem
你明确规定 T
类型是 QList<int>
所以调用不符合你的QList
专业化
template <typename T>
QJsonValue toJsonValue(const QList<T> &source, convertType<QList<T>>)
其中模板参数T
是int
,QList
的模板参数。
您的呼叫只能匹配后备专业化
template <typename T>
QJsonValue toJsonValue(const T &source, convertType<T>)
建议:让模板演绎吧。我的意思是:不要在内部调用中显式显示模板参数
template<typename T>
QJsonValue toJsonValue(const T &source) {
return toJsonValue(source, convertType<T>{});
} // ......^^^^^^^^^^^
// ......^^^^^^^^^^^ no more "<T>" after toJsonValue
所以双参数 toJsonValue()
函数匹配(推导为 int
的带有 T
的 Qlist
版本和带有 [=17= 的后备版本] 推导为 QList<int>
) 但应该选择更专业的版本(QList
版本)。
我试图通过使用 C++ 模板在一定程度上减少样板代码来简化我早期的一些 JSON 序列化。一切顺利,直到我想序列化列表等类型,因为它们本身也是模板,并且似乎需要部分模板专门化,而模板函数似乎不存在。
因此,我应用了在这里找到的一个巧妙的小技巧: https://www.fluentcpp.com/2017/08/15/function-templates-partial-specialization-cpp/
namespace Support {
// These are in header files
template <typename T>
struct convertType{};
// Specialised template that serialises a list by iterating over its members
template <typename T>
QJsonValue toJsonValue(const QList<T> &source, convertType<QList<T>>) {
QJsonArray result;
for (auto it = source.cbegin(); it != source.cend(); it++) {
result.push_back(*it);
}
return result;
}
// "Fallback" template that generates an assertion
template <typename T>
QJsonValue toJsonValue(const T &source, convertType<T>) {
// This function should never be called.
std::string msg = "toJsonValue called with unimplemented type ";
msg += typeid (T).name();
Q_ASSERT_X(false, "toJsonValue<T>", msg.c_str()); // Always asserts.
return QJsonValue();
}
// Convenience function
template<typename T>
QJsonValue toJsonValue(const T &source) {
return toJsonValue<T>(source, convertType<T>{});
}
// This one is in the CPP file
// Integer
template <>
QJsonValue toJsonValue(const int &source, convertType<int>) {
return QJsonValue(source);
}
} // NS Support
当我用整数调用这个模板时如下:
qDebug() << Support::toJsonValue<int>(3713); // if not familiar with Qt, think as qDebug() being the same as std::cout.
如预期的那样输出“QJsonValue(double, 3713)”。
但是,当我尝试按如下方式传递列表时:
QList<int> foo = {1, 2, 3};
qDebug() << Support::toJsonValue<QList<int>>(foo);
在我看来,代码将采用最不专业的函数模板,即生成断言的函数模板。我不知道为什么会发生这种情况,因为我希望它会采用专门用于 QList
.
有人知道为什么会这样吗?我可能滥用了模板吗?
这两个是等价的吗?看起来你创建了一个重载而不是部分专业化。
// Integer
template <>
QJsonValue toJsonValue(const int &source, convertType<int>) {
return QJsonValue(source);
}
// Integer
template <>
QJsonValue<int> toJsonValue(const int &source, convertType<int>) {
return QJsonValue(source);
}
I have no idea why on earth this is happening, as I would expect that it would take the function template specialised for the QList.
Does anyone have an idea why this is happening?
当你打电话时
Support::toJsonValue<QList<int>>(foo);
以下“便捷函数”匹配
template<typename T>
QJsonValue toJsonValue(const T &source) {
return toJsonValue<T>(source, convertType<T>{});
}
和 T
是 QList<int>
。
所以内部调用
toJsonValue<T>(source, convertType<T>{})
成为
toJsonValue<QList<int>>(source, convertType<QList<int>>{});
// ........^^^^^^^^^^^^
// ........^^^^^^^^^^^^ here is the problem
你明确规定 T
类型是 QList<int>
所以调用不符合你的QList
专业化
template <typename T>
QJsonValue toJsonValue(const QList<T> &source, convertType<QList<T>>)
其中模板参数T
是int
,QList
的模板参数。
您的呼叫只能匹配后备专业化
template <typename T>
QJsonValue toJsonValue(const T &source, convertType<T>)
建议:让模板演绎吧。我的意思是:不要在内部调用中显式显示模板参数
template<typename T>
QJsonValue toJsonValue(const T &source) {
return toJsonValue(source, convertType<T>{});
} // ......^^^^^^^^^^^
// ......^^^^^^^^^^^ no more "<T>" after toJsonValue
所以双参数 toJsonValue()
函数匹配(推导为 int
的带有 T
的 Qlist
版本和带有 [=17= 的后备版本] 推导为 QList<int>
) 但应该选择更专业的版本(QList
版本)。