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

时间:2016-06-06 14:17:42

标签: c++ templates

I have the following template function:

MERGE 
   table2 AS target
USING 
   table1 AS source
ON 
   target.id = source.id 
WHEN MATCHED THEN 
   UPDATE SET target.name = source.name
WHEN NOT MATCHED THEN 
   INSERT (id, name) VALUES (source.id, source.name);

Which is in turn used in other template functions, for example:

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

Now I want to specialize ParseValueFromJson for a number of different types, and one of them is a template class (Vector). However using a typical specialization would mean the type parameter to Vector would be undefined:

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

Besides which, in the function implementation I would need the type of T because I'd be using the T typed version of ParseValueFromJson to parse individual items.

And of course if I use template T in the specialization, it is a different function that results in ambiguous calls:

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

So is this possible, or do I need to settle for a separate TryGetContainerFromJson (or similar) function that takes the templated collection type as a second template parameter?

3 个答案:

答案 0 :(得分:4)

我会选择一个实现serializer模板结构来处理序列化(并且可以很容易地部分专门化),作为template function specialization can be troublesome

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

答案 1 :(得分:1)

您无法部分专门化功能模板(obligatory link to 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.
}

答案 2 :(得分:0)

你不能部分地专门化一个函数,但你肯定可以重载它,包括另一个模板化函数。这有效:

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