【问题标题】:Can a template class be used as argument to a template function specialization?模板类可以用作模板函数特化的参数吗?
【发布时间】:2016-06-06 14:17:41
【问题描述】:

我有以下模板函数:

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(或类似)函数,将模板化集合类型作为第二个模板参数?

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    我会选择处理序列化的实现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;
            }
        };
    }
    

    【讨论】:

    • 感谢功能专业化的链接。我倾向于@Smeeheey 的解决方案,但考虑到我可能会重新考虑。
    【解决方案2】:

    您不能部分特化函数模板 (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.
    }
    

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 另外,您可以使用标签调度来保持按值返回。
      • 我很高兴在这种情况下使用输出参数。出于兴趣,为什么这会起作用,而不是返回值?
      • 函数重载只允许返回值类型不同
      • 您当然可以让它们返回您所需的值,只要有一些其他参数来区分重载签名(在您的情况下没有)。正如 TartanLlama 指出的那样,这也可以通过标签调度来实现。
      • 啊当然。谢谢,这里的参数很好:) 一旦我有机会测试运行它,我会选择答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-01
      • 2017-02-03
      • 2011-05-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多