【问题标题】:C++ template function call seems to prefer the less specialized oneC++ 模板函数调用似乎更喜欢不那么专业的调用
【发布时间】:2023-03-31 04:59:02
【问题描述】:

我正在尝试通过使用 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 的函数模板。

有人知道为什么会这样吗?我是否可能滥用模板?

【问题讨论】:

    标签: c++ templates overloading template-specialization template-argument-deduction


    【解决方案1】:

    我不知道为什么会发生这种情况,因为我希望它会采用专门用于 QList 的函数模板。

    有人知道为什么会这样吗?

    当你打电话时

    Support::toJsonValue<QList<int>>(foo);
    

    以下“便利功能”匹配

    template<typename T>
    QJsonValue toJsonValue(const T &source) {
        return toJsonValue<T>(source, convertType<T>{});
    }
    

    TQList&lt;int&gt;

    所以内部调用

    toJsonValue<T>(source, convertType<T>{})
    

    成为

    toJsonValue<QList<int>>(source, convertType<QList<int>>{});
    // ........^^^^^^^^^^^^
    // ........^^^^^^^^^^^^  here is the problem
    

    您明确规定T 类型为QList&lt;int&gt;

    所以调用无法匹配您的 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() 函数都匹配(Qlist 版本与T 被推断为int 和与T 的后备版本被推断为QList&lt;int&gt;)但更多应该选择专门的(QList 版本)。

    【讨论】:

    • 啊,有道理!在我的问题的 QList overloop 的 for 循环中,我错误地使用了 result.push_back(*it);,但它似乎也应该是 result.push_back(toJsonValue(*it, convertType&lt;T&gt;{})); 而不是 result.push_back(toJsonValue&lt;T&gt;(*it, convertType&lt;T&gt;{}));。非常感谢您的详细回答,我为这个问题纠结了好几个小时,但对我来说没有任何意义。
    【解决方案2】:

    这两个是等价的吗?似乎您创建了重载而不是部分特化。

    // 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);
    }
    

    【讨论】:

    • QJsonValue 不是模板类,据我所知,因此向 QJsonValue 添加模板参数应该不起作用,对吧?
    • 抱歉,我说的是QJsonValue toJsonValue&lt;int&gt;(const int &amp;source, convertType&lt;int&gt;) 。但我认为这是在您的便利功能中,T 将固定为QList&lt;int&gt; 而不是int,因此无法匹配专用的。试试toJsonValue(source, convertType&lt;T&gt;{})
    猜你喜欢
    • 1970-01-01
    • 2021-06-18
    • 1970-01-01
    • 2022-10-15
    • 2023-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多