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?
答案 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);