【问题标题】:Why is there no std::is_transparent equivalent for unordered containers?为什么无序容器没有 std::is_transparent 等价物?
【发布时间】:2015-10-27 16:21:31
【问题描述】:

C++14 引入了 Compare::is_transparent 以在关联容器中实现等效的 find 操作。

template< class K > iterator       find( const K& x );
template< class K > const_iterator find( const K& x ) const;

查找具有与值 x 相当的键的元素。 此重载仅参与重载决议,如果 qualified-id Compare::is_transparent 是有效的并且表示一个类型。它 允许在不构造 Key 实例的情况下调用该函数

由于不再构造 Key 的临时实例,因此这些可以更有效。

似乎没有无序容器的等效项

为什么没有Compare::key_equal/Compare::hash_equal

我想允许有效地查找,例如,无序容器中的字符串文字会相对简单?

template<>
struct hash<string>
{
    std::size_t operator()(const string& s) const 
    {
        return ...;
    }
    // hash_equal=true allows hashing string literals
    std::size_t operator()(const char* s) const
    {
        return ...;
    }
};

【问题讨论】:

标签: c++ c++14


【解决方案1】:

比较相等的键应该产生相同的哈希值。解耦散列函数和谓词,同时使一个或两个异构,可能太容易出错。

最近的论文P0919r2 提出了以下示例:

std::hash<long>{}(-1L) == 18446744073709551615ULL
std::hash<double>{}(-1.0) == 11078049357879903929ULL

虽然-1L-1.0 比较相等,但某些异构哈希函数不符合所选相等比较逻辑,可能会产生不同的值。论文添加了支持异构查找的功能模板—— findcountequal_­rangecontains -- 但在满足以下要求时使它们可用 [unord.req]/p17:

如果 qualified-id Hash::transparent_­key_­equal 是有效的并且表示一个类型 ([temp.deduct]),那么程序是非良构的:

  • qualified-id Hash::transparent_­key_­equal::is_­transparent 无效或不表示类型,或
  • Predequal_­to&lt;Key&gt;Hash::transparent_­key_­equal 是不同的类型。

成员函数模板 findcountequal_­rangecontains 不应参与重载决议,除非 qualified-id Hash::transparent_­key_equal 有效并表示类型([temp.deduct])。

在这种情况下,Hash::transparent_­key_­equal 会覆盖默认谓词 (std::equal_to&lt;Key&gt;) 并用于(透明)相等性检查,与 Hash 本身一起用于(透明)散列。

在这些条件下,可以使用以下透明函数对象来启用异构查找:

struct string_equal
{
    using is_transparent = void;

    bool operator()(const std::string& l, const std::string& r) const
    {
        return l.compare(r) == 0; 
    }

    bool operator()(const std::string& l, const char* r) const 
    {
        return l.compare(r) == 0; 
    }

    bool operator()(const char* l, const std::string& r) const
    {
        return r.compare(l) == 0; 
    }
};

struct string_hash
{
    using transparent_key_equal = string_equal; // or std::equal_to<>

    std::size_t operator()(const std::string& s) const
    {
        return s.size();
    }

    std::size_t operator()(const char* s) const
    {
        return std::strlen(s);
    }
};

string_equalstd::equal_to&lt;&gt; 都是透明的比较器,可以用作transparent_key_equalstring_hash

在散列函数类定义中具有此类型别名(或类型定义本身)明确它是一个有效的谓词,可以与特定的散列逻辑一起正常工作,而两者不能发散。这样一个无序集可以声明为:

std::unordered_set<std::string, string_hash> u;

或:

std::unordered_set<std::string, string_hash, string_hash::transparent_key_equal> u;

将使用string_hashstring_equal

【讨论】:

  • 你知道,这个例子在C++17中可以用std::string_view简化。
  • @Deduplicator 是的,实现是为了演示目的
  • P0919r3 在圣地亚哥被选为 C++20。 (见Herbs Trip Report
  • 注意!提供的解决方案尚未找到主流编译器的方式!我刚刚花了一个半小时试图理解为什么我的异构查找不起作用(pastebin.com/ic2NZpmw)。然后我突然想检查这个功能是否已经实现。 P0919R3 既没有在 GCC gcc.gnu.org/projects/cxx-status.html 中实现,也没有在 clang libcxx.llvm.org/cxx2a_status.html 中实现。似乎所描述的解决方案有效的原因是char *string 之间存在隐式转换,因此示例编译。
  • 另外,看起来transparent_key_equalHash 类型中已经被is_transparent 取代,将当前文档(en.cppreference.com/w/cpp/named_req/…) 与以前的版本(webcache.googleusercontent.com/…) 进行比较。 @PiotrSkotnicki 您介意编辑您的答案以提及此功能尚未实现并且transparent_key_equal 可能会被is_transparent 取代吗?
【解决方案2】:

如果您观看来自 CppCon 的 Grill the committee 视频,他们会解释为什么会发生这种情况:没有人为此而战。

C++ 由委员会标准化,但该委员会需要社区的意见。必须有人写论文、回应批评、参加会议等等……然后才能对该功能进行投票。该委员会不只是坐在那里发明语言和库功能。它只对提交给它的内容进行讨论和投票。

【讨论】:

  • 我不知道是哪个提案带来了is_transparent,但我认为这是对包含无序容器的提案的一个相对较小的增强。我想知道为什么在审核过程中没有提出这个问题?
  • 那么你会说这没有技术上的原因不能实现,而是没有人为它而战?
猜你喜欢
  • 2013-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-23
  • 1970-01-01
  • 2016-10-18
  • 2015-11-03
相关资源
最近更新 更多