特定type_trait向量的类的专门化

时间:2017-12-24 22:41:48

标签: c++ templates vector template-specialization

我正在尝试将hash专门化为包含所有算术类型的std :: vector,但它会抛出一些错误

./includes/helpers.hpp:14:22: error: default template argument in a class template partial specialization
      typename = std::enable_if_t<std::is_arithmetic<dtype>::value> >
                 ^
./includes/helpers.hpp:16:8: error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used [-Wunusable-partial-specialization]
struct hash<std::vector<dtype> >
       ^~~~~~~~~~~~~~~~~~~~~~~~~

我尝试尽可能接近不同的enable_if_t指南。但它似乎没有起作用,我做错了什么?

似乎无需使用enable_if_t即可工作。但是那时可能会与不应该使用此哈希的向量发生冲突

这是我的代码到目前为止(编辑得更“完整”)

#include <iostream>
#include <type_traits>
#include <vector>

namespace std {

    template <typename dtype,
              typename = std::enable_if_t< std::is_arithmetic<dtype>::value> >

    struct hash<std::vector<dtype> > {
        size_t operator()(const std::vector<dtype> &input)
        {
            //perform hash
        }
    };
}

using namespace std;

int main()
{
    const vector<int> i{1,2,3,4};
    cout << hash<vector<int>>()(i) << endl;

    return 0;
}

2 个答案:

答案 0 :(得分:1)

问题是,std::hash只有一个模板参数,您无法在部分特化中添加其他默认模板参数。因此,您有多种选择,具体取决于您对哈希的处理方式。

请重新考虑您的方法。这个comment by Yakk非常有用:

  

除非专业化取决于用户提供的类型,否则您可能不会在std中专门化模板。 - Yakk

您可以通过不将自己的hash放入std命名空间来轻松解决此问题。

  1. 您只需从模板参数列表中删除enable_if并将其替换为

    static_assert(std::is_arithmetic<dtype>::value, "!");
    
    在结构体中

    ,假设你只想要算术类型的矢量。

  2. 呼叫运营商的SFINAE。这样,您必须为同一结构中的所有其他向量类型提供所有哈希方法。此外,你必须经历一些重复模板参数的有趣业务,以使编译器满意。您的SFINAE标准是互斥的,这一点非常重要,否则您将会遇到可怕的错误。

    #include <iostream>
    #include <string>
    #include <type_traits>
    #include <vector>
    
    namespace std {
    
    template< typename dtype >
    struct hash< std::vector<dtype> >
    {
        template< typename T = dtype >
        std::enable_if_t<std::is_arithmetic<T>::value, size_t>
        operator()(const std::vector<T> &input) const
        {
            constexpr size_t FNV_prime = 1099511628211ul;
            constexpr size_t FNV_offset = 14695981039346656037ul;
    
            size_t hashed = FNV_offset;
            for(const auto &n:input)
            {
                hashed ^= n;
                hashed *= FNV_prime;
            }
            return hashed;
        }
    
        template< typename T = dtype >
        std::enable_if_t<!std::is_arithmetic<T>::value, size_t>
        operator()(const std::vector<T> &input) const
        {
            std::cout << "No hash for you :-(\n";
            return 0;
        }
    };
    
    } // namespace std
    
    
    int main() {
        {
            std::vector<int> v{1,2,3,4};
            size_t hash = std::hash<std::vector<int>>{}(v);
            std::cout << hash << "\n";
        }
    
        {
            std::vector<std::string> v{"Hello", "world!"};
            size_t hash = std::hash<std::vector<std::string>>{}(v);
            std::cout << hash << "\n";
        }
    }
    

    Live example

  3. 您还可以声明自己的哈希结构,并根据需要添加任意数量的模板参数。然后,您只需要std::hash继承自定义结构。

    #include <iostream>
    #include <string>
    #include <type_traits>
    #include <vector>
    
    template < typename T, bool = std::is_arithmetic<T>::value >
    struct vector_hash;
    
    template < typename T>
    struct vector_hash<T,true> {
        size_t operator()(std::vector<T> const &input) const
        {
            constexpr size_t FNV_prime = 1099511628211ul;
            constexpr size_t FNV_offset = 14695981039346656037ul;
    
            size_t hashed = FNV_offset;
            for(const auto &n:input)
            {
                hashed ^= n;
                hashed *= FNV_prime;
            }
            return hashed;
        }
    };
    
    
    template < typename T>
    struct vector_hash<T,false> {
        size_t operator()(std::vector<T> const &) const
        {
            std::cout << "No hash for you :-(\n";
            return 0;
        }
    };
    
    namespace std {
    
    template< typename dtype >
    struct hash< std::vector<dtype> > : vector_hash<dtype> {};
    
    } // namespace std
    
    
    int main() {
        {
            std::vector<int> v{1,2,3,4};
            size_t hash = std::hash<std::vector<int>>{}(v);
            std::cout << hash << "\n";
        }
    
        {
            std::vector<std::string> v{"Hello", "world!"};
            size_t hash = std::hash<std::vector<std::string>>{}(v);
            std::cout << hash << "\n";
        }
    }
    

    Live example

答案 1 :(得分:1)

在命名空间std中专门化模板是非法的,除非您在用户提供的类型上执行此操作。 Vector不是用户提供的。

您需要做的是:

namespace helper{
  template<class T, class=void>
  struct hash:std::hash<T>{};
}

现在你可以用帮助者的哈希来做常见的sfinae技巧,或者不加考虑地扩展它。

namespace helper {
  template <typename dtype>
  struct hash<std::vector<dtype>,
     std::enable_if_t< std::is_arithmetic<dtype>::value> 
  > {
    size_t operator()(const std::vector<dtype> &input) const {
        //perform hash
    }
  };
}

只需传递helper::hash<T>代替std哈希。额外的void默认参数允许sfina专门化,基本规范转发std哈希,并且没有不良形式问题。