我正在尝试将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;
}
问题是,std::hash
只有一个模板参数,你不能在部分特化中添加额外的默认模板参数。因此,您有多种选择,具体取决于您对哈希的处理方式。
还请重新考虑您的方法。这个comment by Yakk非常有用:
除非专门化取决于用户提供的类型,否则您可能不会在std中专门化模板。 - Yakk
您可以通过不将自己的hash
放入std
命名空间来轻松解决此问题。
enable_if
并将其替换为
static_assert(std::is_arithmetic<dtype>::value, "!");
在struct体中,假设您只想要算术类型的哈希向量。#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 examplestd::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在命名空间std中专门化模板是非法的,除非您在用户提供的类型上执行此操作。矢量不是用户提供的。
你需要做的是:
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 hash。额外的void
默认参数允许sfina专门化,基本规范转发std哈希,并且没有不良形式问题。