【问题标题】:Specialization of class for vectors of specific type_trait特定 type_trait 向量的类特化
【发布时间】:2017-12-24 22:41:47
【问题描述】:

我正在尝试专门化哈希以包含所有算术类型的 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;
}

【问题讨论】:

  • 我从来没有遇到过这样的enable_if_t 应用程序作为未命名模板参数的默认值。您能否提供您提到的指南的链接?
  • @Omni 这不是一个真正的指南,但我只是使用了两个不同的来源,希望它能正常工作。对于“未命名模板的默认值”en.cppreference.com/w/cpp/types/enable_if 说它应该可以工作(向下滚动到数字 5)。
  • 为什么不把static_assert(std::is_arithmetic&lt;dtype&gt;::value, "!");放在结构体中?
  • @RSahu 希望这更完整

标签: c++ templates vector template-specialization


【解决方案1】:

问题是,std::hash 只有一个模板参数,您不能在部分特化中添加额外的默认模板参数。因此,您有多种选择,具体取决于您要对哈希执行的操作。

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

除非特化依赖于用户提供的类型,否则您不能在 std 中特化模板。 – 牦牛

您可以通过不将自己的 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

【讨论】:

  • @Michael 我添加了另一种基于继承的方法。
  • 这个答案建议编写一个格式错误的程序,在 1 和 2 中不需要诊断。除非专业化依赖于用户提供的类型,否则您可能不会在 std 中专门化模板。
  • @HenriMenke 谢谢你的帮助。它非常彻底,现在我看到这个问题似乎有点明显。我想知道为什么它不适用于哈希,但我记得它在其他情况下也有效。
  • 很酷的解决方案。然而,我发现在 C++ 基本类型启用/禁用(我认为这是模板编程的基本操作)中,我感到不安,这仍然感觉像是一种 hack。
  • @Yakk 是的,确实如此。我已将此添加到我的答案中。 TBH,我自己不会使用这个,而是使用boost::hash_value
【解决方案2】:

在命名空间 std 中专门化模板是非法的,除非你在用户提供的类型上这样做。向量不是用户提供的。

你需要做的是:

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

现在您可以使用 helper 的散列做通常的 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&lt;T&gt; 代替 std 哈希。额外的void defaulted 参数允许sfina 特化,base spec 转发std hash,不存在格式错误的问题。

【讨论】:

    猜你喜欢
    • 2014-10-10
    • 1970-01-01
    • 1970-01-01
    • 2015-12-03
    • 2017-12-05
    • 1970-01-01
    • 2017-02-21
    • 2015-03-20
    • 1970-01-01
    相关资源
    最近更新 更多