提问者:小点点

为用户定义类型的shared_ptr专门化std库函数合法吗?


标准说下面关于专门化模板从标准库(通过什么可以和不能我专门化std命名空间?)

只有当声明依赖于用户定义的类型并且专门化满足原始模板的标准库要求并且没有被明确禁止时,程序才可以将任何标准库模板的模板专门化添加到命名空间std。

使用专门用于用户定义类的标准库类来专门化标准库模板是否合法?

例如,将 std::hash 专用为 std::shared_ptr

从阅读上面的段落和链接的问题来看,听起来应该是这样,因为专业化的声明依赖于MyType,但是“除非明确禁止”让我有点担心。

下面的示例按预期编译和工作(AppleClang 7.3),但它合法吗?

#include <unordered_set>
#include <memory>
#include <cassert>
#include <string>

struct MyType {
    MyType(std::string id) : id(id) {}
    std::string id;
};

namespace std {
    template<>
    struct hash<shared_ptr<MyType>> {
        size_t operator()(shared_ptr<MyType> const& mine) const {
            return hash<string>()(mine->id);
        }
    };

    template<>
    struct equal_to<shared_ptr<MyType>> {
        bool operator()(shared_ptr<MyType> const& lhs, shared_ptr<MyType> const& rhs ) const {
            return lhs->id == rhs->id;
        }
    };
}

int main() {
    std::unordered_set<std::shared_ptr<MyType>> mySet;
    auto resultA = mySet.emplace(std::make_shared<MyType>("A"));
    auto resultB = mySet.emplace(std::make_shared<MyType>("B"));
    auto resultA2 = mySet.emplace(std::make_shared<MyType>("A"));
    assert(resultA.second);
    assert(resultB.second);
    assert(!resultA2.second);
}

共1个答案

匿名用户

是的,这是合法的。

甚至专门化< code>std::shared_ptr也是不合法的

请注意,这是全局使用的散列的糟糕实现。首先,因为它不支持空共享指针。其次,因为像往常一样散列共享指针的int值是有问题的。这甚至是危险的,因为如果容器中指向int的共享指针发生了int更改,你就破坏了程序。

考虑为这类情况制作自己的哈希器。

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

  namespace adl_helper {
    template<class T>
    std::size_t hash( T const& t, ... ) {
      return ::notstd::hasher_impl<T>{}(t);
    }
  };
  namespace adl_helper2 {
    template<class T>
    std::size_t hash_helper(T const& t) {
      using ::notstd::adl_helper::hash;
      return hash(t);
    }
  }
  template<class T>
  std::size_t hash(T const& t) {
    return ::notstd::adl_helper2::hash_helper(t);
  }

  struct hasher {
    template<class T>
    std::size_t operator()(T const& t)const {
      return hash(t);
    }
  };

}

现在,这允许3点定制。

首先,如果您覆盖了< code>std::size_t hash(T const

如果做不到这一点,如果你专注于notstd::hasher_impl

第三,如果这两个都失败,它调用< code>std::hash

然后您可以执行以下操作:

std::unordered_set<std::shared_ptr<MyType>, ::notstd::hasher> mySet;

并补充:

struct MyType {
  MyType(std::string id) : id(id) {}
  std::string id;
  friend std::size_t hash( MyType const& self) {
    return ::notstd::hash(self.id);
  }
  friend std::size_t hash( std::shared_ptr<MyType> const& self) {
    if (!self) return 0;
    return ::notstd::hash(*self);
  }
};

这应该会给你一个关于的智能哈希shared_ptr

这就避免了有人在shared_ptr上更改id的危险

共享状态是魔鬼;如果你真的担心复制这些东西会很昂贵,那么考虑在写指针上写一个副本。