【问题标题】:Template specialization for templatized types模板类型的模板特化
【发布时间】:2016-01-01 17:51:58
【问题描述】:

我对 c++ 模板编程领域还很陌生。

我知道我可以专门化函数模板,例如在我的情况下,我想使用 ADL 使用 toJson-function 模板。

例如

template<typename T>
Json::Value toJson(const T&);

其中 Json::Value 由 JsonCPP-Library 提供。

现在我可以将它专门用于像这样的“正常”类型:

template<>
Json::Value toJson<MyClass>(const MyClass&)

太棒了。

但是,我有一个类型,KalmanFilter&lt;3, 3, 3, double&gt;(显然,这是完全参数化的),它又具有几个 Eigen3-Matrices 作为类型,其维度是根据 KalmanFilter 中的积分模板参数确定的。我想像这样专注于 toJson:

template<>
Json::Value toJson<KalmanFilter<int, int, int, T>>(const KalmanFilter<int, int, int, T> ...)

也许有一种不同的方式来实现这一点(例如,Eigen 重载了 std::ostream 运算符

或者我必须使用不同声明的 toJson,比如

template<int A, int B, int C, typename FloatT>
Json::Value toJson(const KalmanFilter<A, B, C, FloatT>&);

?

【问题讨论】:

  • 也许将 toJson 方法的一部分包含在您需要专门化的类型中?
  • 是的,你需要使用不同声明的 toJson,这相当于重载它(这是优越的,除非 toJson 像 toJson&lt;T&gt; 这样使用会破坏它)。
  • 这只对第一个“层”有帮助,卡尔曼滤波器(它会很丑,因为它会相当侵入......但我可以忍受它)。但是,Eigen3::Matrix&lt;...&gt;types 的 toJson 仍然会丢失。
  • @JohannesSchaub-litb 我不完全理解您的评论 - 在这种情况下,优越是什么意思,toJson&lt;T&gt; 是什么意思可能会破坏它?我不是要为 toJson 提供默认实现,因为它会捕获所有其他实例化吗?我完全可以忍受。
  • @deets 重载是优越的,因为你的用例可以通过它来实现,而不能通过专业化(那里没有参数化可能)。 toJson&lt;T&gt;(x) 将中断,因为您的重载需要 int A 而不是类型 T 作为模板参数。然而,通过显式模板参数调用用户可自定义的函数是不好的风格,所以我猜他们不会那样做。如果您自己调用它,无论如何都不是问题。

标签: c++ templates


【解决方案1】:

我将通过使用模板 struct 并专门化它来解决这个问题,而不是专门化函数模板 because of potential drawbacks。

// Hide implementation details in `impl` namespace.
namespace impl
{
    // Forward-declaration of helper converter struct.
    template<typename>
    struct json_converter;

    // Example specialization: `int`.
    template<>
    struct json_converter<int>
    {
        Json::Value to_json(const int&) 
        { 
            // <Conversion logic here.>
        }
    };

    // Example specialization: `KalmanFilter`.        
    template<int A, int B, int C, typename FloatT>
    struct json_converter<KalmanFilter<A, B, C, FloatT>>
    {
        Json::Value to_json(const KalmanFilter<A, B, C, FloatT>&) 
        { 
            // <Conversion logic here.>
        }
    };
}

// Convenient user-interface function, deduces `T`.
template<typename T>
auto to_json(const T& x)
{
    // Instantiate a temporary `json_converter` and call `to_json`.
    return impl::json_converter<std::decay_t<T>>{}.to_json(x);
}

用法:

KalmanFilter<4, 4, 4, double> kf{/* ... */};
auto result = to_json(kf);

【讨论】:

  • 看起来很有趣。我想知道的一件事-这种技术可扩展吗?对于当前的小型项目,我可以接受“不”,但我想为大型项目“注入”更多转换为其他类型。
  • 您可以通过创建json_converter 的额外特化来“注入”额外的转化。这是我的一个项目中的一个真实的example。 JSON转换实现为here。
【解决方案2】:

或者我必须使用不同声明的 toJson,比如

template<int A, int B, int C, typename FloatT>
Json::Value toJson(const KalmanFilter<A, B, C, FloatT>&);

?

是的,这正是您所需要的。

进入函数模板专业化会让您感到困惑。绝对没有理由在这里使用函数模板专业化。如果您想要其他类型的转换,只需以相同的方式添加它们:

 template<double A, double B, double C, typename T>
 Json::Value toJson(const OtherFilter<A, B, C, T>&);

如果您在不同的命名空间中有不同的类型,那么使用具有多个特化的单个函数模板将无法与 ADL 配合使用。每个toJson 函数都需要位于其参数类型的命名空间中。更不用说您可能会对编译器实际选择使用的专业化感到惊讶。普通的重载解决方案并不令人惊讶。

【讨论】:

  • 引入类模板的一个优势是不会有平行世界。其他用户总是可以添加 toJson 的重载,创建具有重载和类模板及其主要重载使用的特化的并行世界。当主重载没有委托给类模板时,平行世界就不可能了,任何人都必须使用适当的重载。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多