模板类是否可以用作模板函数专用化的参数

Can a template class be used as argument to a template function specialization?

本文关键字:专用 参数 函数 是否      更新时间:2023-10-16

我有以下模板函数:

template <class T>
inline T ParseValueFromJson(const JSONValue& jsonValue);

这反过来又用于其他模板函数,例如:

template <class T>
bool TryGetValueFromJson(
    const JSONValue& jsonValue,
    const String& name,
    T& variable)
{
    if (!jsonValue.Contains(name))
    {
        return false;
    }
    variable = ParseValueFromJson<T>(jsonValue[name]);
    return true;
}

现在我想将 ParseValueFromJson 专门用于许多不同的类型,其中之一是模板类 (Vector)。但是,使用典型的专用化意味着 Vector 的类型参数将未定义:

template <>
inline Vector<T> ParseValueFromJson<Vector<T>>(const JSONValue& jsonValue)

除此之外,在函数实现中,我需要 T 的类型,因为我将使用 ParseValueFromJson 的 T 类型版本来解析单个项目。

当然,如果我在专业化中使用模板 T,它是一个不同的函数,会导致不明确的调用:

    template <typename T>
inline Vector<T> ParseValueFromJson<Vector<T>>(const JSONValue& jsonValue)

那么这是可能的,还是我需要满足于一个单独的 TryGetContainerFromJson(或类似)函数,该函数将模板化集合类型作为第二个模板参数?

我会选择处理序列化的serializer模板结构的实现(并且可以很容易地部分专业化),因为模板函数专用化可能很麻烦。

// Default serializer implementation.
namespace impl
{
    template <typename T>
    struct serializer 
    {
        T from_json(const JSONValue& jv)
        { 
            // default implementation...
        }
    };
}
// Convenient interface function.
template <class T>
inline T ParseValueFromJson(const JSONValue& jsonValue)
{
    return impl::serializer<T>{}.from_json(jsonValue);
}
// "Special" serializer implementations.
namespace impl
{            
    template <typename T>
    struct serializer<Vector<T>>
    {
        Vector<T> from_json(const JSONValue& jv)
        { 
            Vector<T> result;
            for(auto c : jv.children()) 
            {
                result.add(ParseValueFromJson<T>(c));
            }
            return result;
        }
    };
}
<</div> div class="answers">

您不能部分专用化函数模板(指向 Herb Sutter 的强制性链接),但您始终可以重载函数模板。只需沿着主节点前进,将类型作为标记参数传递:

template <class > struct tag { };
template <class T>
inline T ParseValueFromJson(const JSONValue& jsonValue) {
    return impl::ParseValueFromJson(jsonValue, tag<T>{});
}

然后提供一堆重载:

namespace impl {    
    template <class T>
    inline T ParseValueFromJson(const JSONValue& jsonValue, tag<T> ) {
        // generic, if that makes sense
    }
    template <class T>
    inline T ParseValueFromJson(const JSONValue& jsonValue, tag<std::vector<T>> ) {
        // vector version
    }
    // etc.
}

您不能部分专用化函数,但是您当然可以重载它,包括使用另一个模板化函数。这有效:

template <typename T>
void func(const T& arg)
{
    std::cout << "General" << std::endl;
}
template <typename T>
void func(const std::vector<T>& vec)
{
    std::cout << "Vector overload" << std::endl;
}
int main()
{
    func(1);                   //outputs "General"
    func(std::vector<int>{1}); //outputs "Vector overload"
    return 0;
}   

在您的情况下,如果您愿意按输出参数返回,您可以调整此解决方案,因此您有 2 个这样的函数:

template <class T>
inline void ParseValueFromJson(const JSONValue& jsonValue, T& output);
template <class T>
inline void ParseValueFromJson(const JSONValue& jsonValue, std::vector<T>& output);

这实际上适用于您的代码,因为您最终已经通过输出参数返回答案。因此,在您的呼叫站点,您只需更改线路即可

variable = ParseValueFromJson<T>(jsonValue[name]);

ParseValueFromJson<T>(jsonValue[name], variable);