boost unordered_set

时间:2015-12-19 11:29:02

标签: c++ boost

当我使用boost :: unordered_set时,我遇到了一种我没想到的行为。我提取了一个最小的例子(不太确定' minimal'尽管:-))来证明我的意思。该程序执行以下操作:

  • 一个班级' edge_type'用字段索引定义'等其他领域
  • 定义了一个比较运算符,如果两个实例的所有字段相等,则返回true
  • 为那些具有自己的哈希函数和谓词的edge_type定义了一个unordered_set来检查等效元素
  • 哈希函数使用所有字段来计算哈希值
  • 谓词仅使用字段'索引'检查等效性
  • ' edge_type'的两个实例使用相同的索引创建但不同的其他字段。
  • 使用operator =()比较两个实例会导致:'不等于'如预期的那样
  • 使用boost :: unordered_set< ...> :: key_eq()比较两个实例会导致:'实例等效'如预期的那样
  • 在插入第一个实例后插入第二个实例导致unordered_set的大小为2

这个尺寸不是我的预期。我认为第二个实例不会被插入,因为它与第一个实例相同。文档说:

  

泼尼松:   一个二元谓词,它接受与元素相同类型的两个参数并返回一个bool。表达式pred(a,b),其中pred是这种类型的对象,a和b是键值,如果a被认为等同于b,则返回true。这可以是实现函数调用操作符的类,也可以是指向函数的指针(请参阅构造函数)。默认为equal_to,返回与应用等于运算符(a == b)相同。   unordered_set对象使用此表达式来确定两个元素键是否相等。 unordered_set容器中没有两个元素可以使用此谓词生成true的键。   别名为成员类型unordered_set :: key_equal。

  

插入:   在unordered_set中插入新元素。   只有当元素不等同于容器中已有的任何其他元素时,才会插入每个元素(unordered_set中的元素具有唯一值)。

所以我没有正确阅读规范,但我无法弄清楚出错的地方。 (该规范来自www.cplusplus.com。我对tr1 :: unordered_set,gcc 4.8.1,boost 1.54有相同的经验。[我们还没有使用C ++ 11工作......]) 。我很欣赏任何关于我去哪里的提示。

示例:

/* File: test_set.cpp
   Compile: g++ -I ${BOOST_INCLUDE} -L ${BOOST_LIB) -lboost_unit_test_framework test_set.cpp -o test_set
   Output:
   Running 1 test case...
   test_set.cpp(110): error in "types_construction_and_operators": check ec.size() == 1 failed [2 != 1]
 *** 1 failure detected in test suite "Master Test Suite" 
*/


#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN

#include <boost/functional.hpp>
#include <boost/unordered_set.hpp>
#include <boost/functional/hash.hpp>
#include <boost/operators.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/test/results_reporter.hpp>
#include <boost/test/output_test_stream.hpp>
#include <boost/test/unit_test_log.hpp>
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/framework.hpp>
#include <boost/test/detail/unit_test_parameters.hpp>
#include <boost/test/utils/nullstream.hpp>
typedef boost::onullstream onullstream_type;

using boost::test_tools::output_test_stream;
using namespace boost::unit_test;

#define BOOST_TEST_MODULE test_unordered_set


struct edge_type;
struct edge_equal_to;
typedef  boost::unordered_set<edge_type, boost::hash<edge_type>, edge_equal_to > edge_collection_type;
std::size_t hash_value( edge_type const& edge );

struct edge_type : public boost::equality_comparable<edge_type> {
   edge_type( std::size_t index, std::size_t f, std::size_t t );
   edge_type( edge_type const& );
   std::size_t index;
   std::size_t from_node_index;
   std::size_t to_node_index;
   edge_type& operator=( edge_type const& that );
   bool operator==( edge_type const& that ) const;
};

struct edge_equal_to : std::binary_function< edge_type, edge_type, bool > {
   bool operator()( edge_type const& edge1, edge_type const& edge2 ) const;
};



bool edge_equal_to::operator()( edge_type const& edge1,
                                edge_type const& edge2 ) const {
   return edge1.index == edge2.index ;
}

std::size_t hash_value( edge_type const& edge ) {
   std::size_t  seed = 0;
   boost::hash_combine(seed, edge.index );
   boost::hash_combine(seed, edge.from_node_index );
   boost::hash_combine(seed, edge.to_node_index );
   return seed;
}

edge_type::edge_type( std::size_t idx,
                      std::size_t f,
                      std::size_t t ) :
   index(idx), from_node_index(f), to_node_index(t) {
}

edge_type::edge_type( edge_type const& that ) {
   from_node_index = that.from_node_index;
   to_node_index   = that.to_node_index;
   index = that.index;
}

edge_type& edge_type::operator=( edge_type const& that ) {
   if( this != &that ) {
      from_node_index = that.from_node_index;
      to_node_index   = that.to_node_index;
      index = that.index;
   }
   return *this;
}

bool edge_type::operator==( edge_type const& that ) const {
   return index == that.index &&
          from_node_index == that.from_node_index &&
          to_node_index == that.to_node_index;
}


BOOST_AUTO_TEST_SUITE( test_suite_unordered_set )

BOOST_AUTO_TEST_CASE( types_construction_and_operators )
{
   edge_type edge1( 1, 100, 101 );
   BOOST_CHECK_EQUAL( edge1.index, 1 );
   BOOST_CHECK_EQUAL( edge1.to_node_index, 101 );
   BOOST_CHECK_EQUAL( edge1.from_node_index, 100 );

   edge_type edge2( 1, 102, 103 );
   BOOST_CHECK_EQUAL( edge2.index, 1 );
   BOOST_CHECK_EQUAL( edge2.to_node_index, 103 );
   BOOST_CHECK_EQUAL( edge2.from_node_index, 102 );

   BOOST_CHECK( edge1 != edge2 );

   edge_collection_type ec;

   ec.insert( edge1 );
   BOOST_CHECK_EQUAL( ec.size(), 1 );

   BOOST_CHECK_EQUAL( ec.key_eq()( edge1, edge2 ), true );
   ec.insert( edge2 );
   BOOST_CHECK_EQUAL( ec.size(), 1 );  // <---- This test fails !
}

BOOST_AUTO_TEST_SUITE_END()

2 个答案:

答案 0 :(得分:1)

如果两个实例应该被认为是相等的,那么它们也应该具有相同的哈希值。仅当两个实体的散列相同时才使用等式谓词,以确定它们是否真正相等或者是偶然哈希冲突的情况。

http://www.boost.org/doc/libs/1_56_0/doc/html/unordered/hash_equality.html

  

如果您希望使用不同的相等功能,您还需要   使用匹配的哈希函数。例如,实施案例   不敏感的字典,您需要定义不区分大小写的相等   谓词和哈希函数:

答案 1 :(得分:1)

测试失败,因为您已将2个不相等的对象插入到集合中。因此ec.size()应返回2.

这是因为edge_equal的实现仅测试索引的相等性,而哈希函数依赖于所有成员。