Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为满足条件的类专门化“std::hash”_C++_Templates_C++11 - Fatal编程技术网

C++ 为满足条件的类专门化“std::hash”

C++ 为满足条件的类专门化“std::hash”,c++,templates,c++11,C++,Templates,C++11,假设我有一个简单的布尔特征类,MyTrait。也就是说,对于任何类型的T,我都可以执行MyTrait::value并得到true或false。我想专门为所有类型T指定std::hash,其中MyTrait::value为true。有没有办法做到这一点?一些失败的尝试: template <class T, typename std::enable_if< MyTrait<T>::value, int

假设我有一个简单的布尔特征类,
MyTrait
。也就是说,对于任何类型的
T
,我都可以执行
MyTrait::value
并得到true或false。我想专门为所有类型
T
指定
std::hash
,其中
MyTrait::value
为true。有没有办法做到这一点?一些失败的尝试:

template <class T, typename std::enable_if<
                                MyTrait<T>::value, int
                            >::type = 0>
struct hash<T> {
...
}
我还尝试将所有的部分专门化内容放在hash之后,但是对于
T
处于非推断上下文中,会出现一条错误消息

有没有办法做到这一点?之前至少有一个关于SO的问题表明没有:


要么是一个解决方案,要么是一个明确的“否”,然后是一个简短的解释,这都是一个很好的答案。

std名称空间中的给定模板可能专门用于任何用户定义的类型(1)

为了将std::hash专门用于某些类型的T,如果trait是真的,我们可以天真地编写如下内容(注意:不起作用):


不确定它是否合法,但有了C++20的概念,您可能会执行以下操作:

template <typename T>
concept MyConcept = MyTrait<T>::value;

namespace std
{

    template <MyConcept T>
    struct hash<T>
    {
        std::size_t operator()(const T& t) const { /*..*/ }
        // ...
    };

}
模板
概念MyConcept=MyTrait::value;
名称空间标准
{
模板
结构散列
{
std::size_t运算符()(常数t&t)常数{/*../}
// ...
};
}

他们说计算机科学中的所有问题都可以通过另一种间接方式来解决

如果您愿意,我们可以实现Sean Parent的技术,该技术使用类型擦除和少量多态性来委托给自由函数。我们可以对擦除的类型专门化
std::hash

用法如下所示:

template<> struct MyTrait<Foo> : std::true_type{};
template<> struct MyTrait<Bar> : std::true_type{};
// ...
Foo a;
Bar b;
Bad c; // MyTrait<Bad>::value is false

std::cout << std::hash<my_hashable>{}(my_hashable{a}) << std::endl;
std::cout << std::hash<my_hashable>{}(my_hashable{b}) << std::endl;
// compiler error
//std::cout << std::hash<my_hashable>{}(my_hashable{c}) << std::endl;
接下来,我们对类型擦除类的唯一专门化
std::hash

namespace std
{
  template<>
  struct hash<my_hashable>
  {
    std::size_t operator()(const my_hashable& h) const{return do_hash(h);}
  };
}

然后,正如我在本文开头所展示的,我们可以定义我们的类并决定哪些类满足这个特性。在这里,
T
采用了
Foo
Bar
(不是
my\u hashable
,因为我们已经委托
impl\u hashable
来恢复我们在构建
my\u hashable
实例时传入的类型)

,因为
std::hash
只有一个模板参数,您可以执行的唯一一种部分专门化是,例如,
template struct hash{…}
。我看不出有任何方法会导致替换失败,从而禁用专门化。但是,您可以为
std::unordered_map
等提供自定义哈希类型,您可以免费为专门化配置一个伪模板参数。@melak47是的,当然这是一个选项,但为了便于使用,排名不太高。有用的信息,谢谢。我从广义上了解宏观方法;出于通常的原因,我希望避免这样做。你能详细谈谈“不确定它是否合法”吗?这其中哪一部分是非法的?不确定specialize std via概念是否合法。我看到可以用T包装创建std::hash,但是似乎仍然没有std::hash。这种方法是否允许专门化MyTrait的类型为true,直接用于std::unordered_集合中,而不必在集合的模板参数中指定非默认哈希类型?@jtbandes:不幸的是,不允许。我们可以创建一个版本的
my\u hashable
,它拥有底层的
T
,然后我们可以创建一个
无序集。我意识到间接寻址的附加级别并不理想,因此在我的帖子顶部有资格。我熟悉SP多态性,也就是说,这并不能回答问题,尽管我很欣赏这一努力。这并不是专门针对不符合某个特征,而是针对其他类型。这是至关重要的,因为这个练习的目标,实际上我能想到的专门化std::hash的唯一原因,是使std::unordered_映射/集“正常工作”。我只能使用你的解决方案,如果我将密钥类型
my_hashable
,这会产生很多不希望的结果。@NirFriedman:当然。我同意你的看法。我倾向于说Jarod42的答案是正确的,尽管事实上标准并没有真正允许或禁止它。根据特征的不同,标准库中的某个类型可能是真的,这可能是非法的。否则,我们的手就有点被宏或类型擦除所束缚了。@AndyG是的,我想我应该确保,对于我无法控制的类型,这个特征永远不会计算为真。在这个特定的例子中,这个特征是从我的一个类型继承的,所以我认为在这方面是安全的。
template <typename T>
concept MyConcept = MyTrait<T>::value;

namespace std
{

    template <MyConcept T>
    struct hash<T>
    {
        std::size_t operator()(const T& t) const { /*..*/ }
        // ...
    };

}
template<> struct MyTrait<Foo> : std::true_type{};
template<> struct MyTrait<Bar> : std::true_type{};
// ...
Foo a;
Bar b;
Bad c; // MyTrait<Bad>::value is false

std::cout << std::hash<my_hashable>{}(my_hashable{a}) << std::endl;
std::cout << std::hash<my_hashable>{}(my_hashable{b}) << std::endl;
// compiler error
//std::cout << std::hash<my_hashable>{}(my_hashable{c}) << std::endl;
class my_hashable
{
  public:
  template <class T>
  my_hashable(T& x) : self_(std::make_shared<impl_hashable<T>>(&x))
  {}

  friend std::size_t do_hash(const my_hashable& x)
  {
      return x.self_->hash_();
  }

  private:
  struct base_hashable
  {
    virtual ~base_hashable() = default;
    virtual std::size_t hash_() const = 0;
  }; // base_hashable

  template <class T>
  struct impl_hashable final : base_hashable
  {
    impl_hashable(T* x) : data_(x) { }
    std::size_t hash_() const override
    {
        return do_hash(*data_); // call to free function
    }

    T* data_;
  }; // impl_hashable

 std::shared_ptr<const base_hashable> self_;
}; 
namespace std
{
  template<>
  struct hash<my_hashable>
  {
    std::size_t operator()(const my_hashable& h) const{return do_hash(h);}
  };
}
template<class T>
std::size_t do_hash(const T& t)
{
    static_assert(MyTrait<T>::value, "Can only call do_hash for types for which MyTrait is true");
    return std::hash<typename T::data_t>{}(t.data);
}