为什么std::unordered_set::find只支持以key查找?

youngwolf 2016-11-16 01:06:24
以我的愚见,std c++的一些基本行为,虽然细节不甚了解,但为何设计成那个样子,基本上还是明白的,除了这个std::unordered_set::find,由于它只能以key为查找,如果存的是对象(而非指针),为了查找一个对象,我还得先构造这个对象,这明显不合理。有人会说那用std::unordered_map啊,但这样一来,std::unordered_set是否显得有些多余?

我心中的unordered_set应该是boost::container::unordered_set那样:
template<typename KeyType, typename KeyHasher, typename KeyEqual>
iterator find(const KeyType &, KeyHasher, KeyEqual);

可以看到它支持以任意key type为key来查找对象。
...全文
930 36 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
36 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
引用 33 楼 yang79tao 的回复:
[quote=引用 30 楼 akirya 的回复:] [quote=引用 28 楼 yang79tao 的回复:] 这能重载吗?外面的代码已经写好了,比如使用代码是: do_something_to_all([](const auto& item) {item->show_info("", "");}); 要如何重载do_something_to_all才能让上面的代码调用到新的do_something_to_all?
这样写重载版不就行了嘛

template<typename K, typename V, typename _Predicate>
void do_something_to_all( std::map<K, V>& __can, const _Predicate& __pred )
{
    for ( auto& item : __can ) __pred( item.second );
}
完整测试代码
#include <vector>
#include <map>
#include<stdio.h>
using namespace std;


struct test
{
    int value;
    test( int i = 0 ): value( i ) {}
    void show_info( const char*, const char* )const
    {
        printf( "%d\n", value );
    }
};

template<typename _Can, typename _Predicate>
void do_something_to_all( _Can& __can, const _Predicate& __pred )
{
    for ( auto& item : __can ) __pred( item );
}

template<typename K, typename V, typename _Predicate>
void do_something_to_all( std::map<K, V>& __can, const _Predicate& __pred )
{
    for ( auto& item : __can ) __pred( item.second );
}

int main( int argc, char const *argv[] )
{
	auto func =  []( const auto & item )
    {
        item.show_info( "", "" );
    } ;
	
    std::map<int, test> m1;
    m1[1] = test( 111 );
    m1[9] = test( 99 );
    do_something_to_all( m1,func );

    std::vector<test> v1;
    v1.push_back( test( 222 ) );
    v1.push_back( test( 333 ) );
    do_something_to_all( v1, func );
    return 0;
}
[/quote] 嗯,你这个确实能工作,作为free function的话,让编译器来选择,只是我需要需要的是类成员函数,因为我在do_something_to_all中需要lock一个mutex,于是我定义一个宏,并在类里面使用这个宏,这个宏就不能重载了,所以我还是写了两份代码,名字一样。[/quote] 类成员函数一样可以重在啊。
youngwolf 2016-11-21
  • 打赏
  • 举报
回复
引用 32 楼 ri_aje 的回复:
[quote=引用 29 楼 yang79tao 的回复:] [quote=引用 26 楼 ri_aje 的回复:] [quote=引用 21 楼 yang79tao 的回复:] [quote=引用 19 楼 pengzhixi 的回复:] 通用性优先。至于你所说的困惑,如果你单单限定使用unorder_set那只能是说你自己不够灵活。你想把Key的比较简单化,不涉及真正的value比较 标准库完全有其他容器达到你的需求。
如果在一个unorderd_set存放复杂对象,提供hasher和equal 几乎是必须的
引用 18 楼 ri_aje 的回复:
[quote=引用 16 楼 yang79tao 的回复:] [quote=引用 15 楼 fefe82 的回复:] [quote=引用 14 楼 fefe82 的回复:] [quote=引用 13 楼 yang79tao 的回复:] [quote=引用 12 楼 fefe82 的回复:] [quote=引用 7 楼 yang79tao 的回复:] [quote=引用 5 楼 fefe82 的回复:] [quote=引用 3 楼 yang79tao 的回复:] [quote=引用 1 楼 paschen 的回复:] std::unordered_set 的 key 就是value std::unordered_map 本来就是以key来查找value而设计,如果要以value来查找,就需要一个遍历所有元素,一个个比较
你要是遇到我的难处就知道我的意思了,举例: class A //非常复杂一个类 { public: int id() const {return id_;} protected: ... }; //实现一个harsher,直接返回A::id() //实现一个equal,直接返回object1.id() == object2.id() std::unordered:set<A, harsher, equal> my_set; //根据id (100)查找对象: my_set.find(...); //如何写? [/quote] 你这个为什么不用 unordered_map 呢 ...[/quote] 只是讨论问题,对于你的提问,我只能回答:如果你unordered_set实现好了,能用set我为什么要用map呢?相比于用map,用set不会带来任何可读性、健壮性、代码量等等的损失,那我能省一点是一点啊(set比map省总是没问题的吧)。 而且最重要的一点,map是二维的,set是一维的,我有很多通用的一维窗口操作函数(直接操作*iterator),这些函数将统统不能用于操作map。[/quote] 不同的工具有不同的用处。 我们在用容器的时候,通常是根据我们需要的操作选择一个合适的容器。如果这个容器不合适,就换一个。如果 std 的容器不合适,但是 boost 的容器合适,那么用 boost 的也无妨。 但是容器设计的时候,是没有办法满足所有人的所有需求的。作为使用者,只能适应现有的容器,要不就自己写一个。 至于这个容器为啥这样设计,就得期待高人解释一下。 个人感觉,boost 那个接口,提供 keyhash 和 keyequal 也并不简单,并且要对这两个加很多约束才行。 比如 keyhash(key) == hash(value) 当且仅当 keyequal(key, value) == true 。 这有在某些特殊的情况下,提供这样两个函数才比较容易。你的这个就是个例子。[/quote] 那你就错了,unordered_set本来就需要那两个函数,你看看构造函数,unordered_map也一样,你未指定只是给你选择了一个默认的。那两个函数是不能少的,不管容不容易都得提供那两个函数。[/quote] unordered_set 需要的是 hash 和 equal 都是对 value 用的。 这里需要的另外的两个。 如果还用那两个的话,估计就还要构造对象。 [/quote] 来自:http://www.boost.org/doc/libs/1_62_0/doc/html/boost/unordered_set.html unordered_set lookup iterator find(key_type const& k); const_iterator find(key_type const& k) const; template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq); template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> const_iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const; Returns: An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. Notes: The templated overloads are a non-standard extensions which allows you to use a compatible hash function and equality predicate for a key of a different type in order to avoid an expensive type cast. In general, its use is not encouraged.[/quote] 这个我知道,这就是我的问题,为什么呢?boost的实现我觉得带来了很多好处,而且find有多个重载版本,你仍然可以使用以key为参数的查找,多一个功能何乐而不为呢?[/quote] 主要是很难明确清晰的界定相关类型间效果一致的 hash。 比如对于 unordered_set<unique_ptr<T*>>,标准提供 unique_ptr 的 hash,具体怎么做各家都可以不一样,你作为使用者看不到,并且也不应该看到。如果你需要用裸指针 T* 寻找 unordered map 里对应的 unique_ptr,通过 find 支持自定义 hash 实现,这个你自己提供的 hash,怎么保证对于同样的 T* 和 unique_ptr<T*> 一定能得到同样的 hash 值呢*(要求 unique_ptr 必须按裸指针 hash 可能是一种方法,但标准并不想把实现限制的这么死)?强行支持这种 find,基本没法写通用代码了,因为你提供的 hash 要想能够达到正确的效果,它的行为必然要保持和特定 stl unique_ptr 的 hash 的实现一致,最后代码就变成一坨 #ifdef。 诚然,对于主楼的用例,unordered_set 定义时的 hash 和 find 使用时的 hash 全是你控制,还算是在效果上可以保持一致。但从标准的角度讲,很难界定这两个不同的 hash 之间的行为。另外标准也不可能专门为你的特殊需求提供支持,要想进 stl 就必须具有普遍的适用性。[/quote] 另外,如果在一个unorderd_set存放复杂对象,提供hasher和equal 几乎是必须的,从这一点上来说,unordered_set只能选择相信用户提供的hasher和equal,那么既然在构造的时候可以提供hasher和equal,那在find的时候也可以提供,如果要说可能用户的hasher和equal有逻辑错误,那构造的时候的hasher和equal,unorderd_set还不是欣然接受了吗?[/quote] 根据我的理解,界定用户自定义 hash 的行为比较容易,标准已经做到了;界定两个 hash 产生等同的效果比较困难。你说的这种情况,应该属于前者的范畴,不能用此证明后者也是可行的。比如你主楼的用例,具体要求就是 KeyHasher(KeyType) == ContainerHasher(A.id) 当且仅当 KeyType 表示的东西(可以就是个 id,也可以是另一个重量级对对象,但我们只用它里面的某些字段组装个具有 id 功能的东西)等同于 A.id 的时候。这里面模糊信息太多,自由度太大,目前标准还没想出好方法用清晰明了的技术语言表述之,并且保证各编译器厂商能够准确无误的实现统一行为。话说回来,你要是有好的方法,完全可以写提案,然后申请扩充或修改标准。其实 std::set/map 的 find 本来面临类似的问题,不过那边只需要定义 < 和 std::less/greater 等等的,容易多了,所以最后特化一下 std::less<void> 就给解决了。我很期待有高人能把 unordered 的 find 问题也解决了。楼主加油哦。[/quote] 如果眼睛里容不得一点沙子的话,这个问题恐怕无解,但至少有人跟我一样有一些不爽,比如boost::container的作者。 [/quote] boost 文档里说 find is not encouraged. 我猜是有人不爽,然后欠佳思考的就加了个重载,使用过程中结果遇到一堆问题,带来的问题比解决的还多,但是已经加进去了,为了兼容历史,一时又撤不掉,所以只好说 not encouraged 的,有点类似于标准的 deprecated。从这点说标准保守的做法反而更好。[/quote] boost是要冒进一些,还有一个典型就是list::splice(iterator where, list& other, iterator begin, iterator end, size_t size) 这个函数可以让splice达到O(n)复杂度,但如果size错了,可能麻烦就大了。
youngwolf 2016-11-21
  • 打赏
  • 举报
回复
引用 30 楼 akirya 的回复:
[quote=引用 28 楼 yang79tao 的回复:] 这能重载吗?外面的代码已经写好了,比如使用代码是: do_something_to_all([](const auto& item) {item->show_info("", "");}); 要如何重载do_something_to_all才能让上面的代码调用到新的do_something_to_all?
这样写重载版不就行了嘛

template<typename K, typename V, typename _Predicate>
void do_something_to_all( std::map<K, V>& __can, const _Predicate& __pred )
{
    for ( auto& item : __can ) __pred( item.second );
}
完整测试代码
#include <vector>
#include <map>
#include<stdio.h>
using namespace std;


struct test
{
    int value;
    test( int i = 0 ): value( i ) {}
    void show_info( const char*, const char* )const
    {
        printf( "%d\n", value );
    }
};

template<typename _Can, typename _Predicate>
void do_something_to_all( _Can& __can, const _Predicate& __pred )
{
    for ( auto& item : __can ) __pred( item );
}

template<typename K, typename V, typename _Predicate>
void do_something_to_all( std::map<K, V>& __can, const _Predicate& __pred )
{
    for ( auto& item : __can ) __pred( item.second );
}

int main( int argc, char const *argv[] )
{
	auto func =  []( const auto & item )
    {
        item.show_info( "", "" );
    } ;
	
    std::map<int, test> m1;
    m1[1] = test( 111 );
    m1[9] = test( 99 );
    do_something_to_all( m1,func );

    std::vector<test> v1;
    v1.push_back( test( 222 ) );
    v1.push_back( test( 333 ) );
    do_something_to_all( v1, func );
    return 0;
}
[/quote] 嗯,你这个确实能工作,作为free function的话,让编译器来选择,只是我需要需要的是类成员函数,因为我在do_something_to_all中需要lock一个mutex,于是我定义一个宏,并在类里面使用这个宏,这个宏就不能重载了,所以我还是写了两份代码,名字一样。
youngwolf 2016-11-21
  • 打赏
  • 举报
回复
引用 35 楼 akirya 的回复:
[quote=引用 33 楼 yang79tao 的回复:] [quote=引用 30 楼 akirya 的回复:] [quote=引用 28 楼 yang79tao 的回复:] 这能重载吗?外面的代码已经写好了,比如使用代码是: do_something_to_all([](const auto& item) {item->show_info("", "");}); 要如何重载do_something_to_all才能让上面的代码调用到新的do_something_to_all?
这样写重载版不就行了嘛

template<typename K, typename V, typename _Predicate>
void do_something_to_all( std::map<K, V>& __can, const _Predicate& __pred )
{
    for ( auto& item : __can ) __pred( item.second );
}
完整测试代码
#include <vector>
#include <map>
#include<stdio.h>
using namespace std;


struct test
{
    int value;
    test( int i = 0 ): value( i ) {}
    void show_info( const char*, const char* )const
    {
        printf( "%d\n", value );
    }
};

template<typename _Can, typename _Predicate>
void do_something_to_all( _Can& __can, const _Predicate& __pred )
{
    for ( auto& item : __can ) __pred( item );
}

template<typename K, typename V, typename _Predicate>
void do_something_to_all( std::map<K, V>& __can, const _Predicate& __pred )
{
    for ( auto& item : __can ) __pred( item.second );
}

int main( int argc, char const *argv[] )
{
	auto func =  []( const auto & item )
    {
        item.show_info( "", "" );
    } ;
	
    std::map<int, test> m1;
    m1[1] = test( 111 );
    m1[9] = test( 99 );
    do_something_to_all( m1,func );

    std::vector<test> v1;
    v1.push_back( test( 222 ) );
    v1.push_back( test( 333 ) );
    do_something_to_all( v1, func );
    return 0;
}
[/quote] 嗯,你这个确实能工作,作为free function的话,让编译器来选择,只是我需要需要的是类成员函数,因为我在do_something_to_all中需要lock一个mutex,于是我定义一个宏,并在类里面使用这个宏,这个宏就不能重载了,所以我还是写了两份代码,名字一样。[/quote] 类成员函数一样可以重在啊。[/quote] 是的,我就是不想修改这个类,现在我的办法跟你的类似,总之重写了另外一个do_somethine_to_all,对外接口保持了不变。 理想的情况是,一个类A,其里面有一个容器,容器的类型由使用者通过模板传递给类A,但类A想实现这个do_something_to_all函数(这个函数本身不应该再需要模板了,因为容器和自己都在同一个类里面),就不好做了。
ri_aje 2016-11-19
  • 打赏
  • 举报
回复
引用 29 楼 yang79tao 的回复:
[quote=引用 26 楼 ri_aje 的回复:] [quote=引用 21 楼 yang79tao 的回复:] [quote=引用 19 楼 pengzhixi 的回复:] 通用性优先。至于你所说的困惑,如果你单单限定使用unorder_set那只能是说你自己不够灵活。你想把Key的比较简单化,不涉及真正的value比较 标准库完全有其他容器达到你的需求。
如果在一个unorderd_set存放复杂对象,提供hasher和equal 几乎是必须的
引用 18 楼 ri_aje 的回复:
[quote=引用 16 楼 yang79tao 的回复:] [quote=引用 15 楼 fefe82 的回复:] [quote=引用 14 楼 fefe82 的回复:] [quote=引用 13 楼 yang79tao 的回复:] [quote=引用 12 楼 fefe82 的回复:] [quote=引用 7 楼 yang79tao 的回复:] [quote=引用 5 楼 fefe82 的回复:] [quote=引用 3 楼 yang79tao 的回复:] [quote=引用 1 楼 paschen 的回复:] std::unordered_set 的 key 就是value std::unordered_map 本来就是以key来查找value而设计,如果要以value来查找,就需要一个遍历所有元素,一个个比较
你要是遇到我的难处就知道我的意思了,举例: class A //非常复杂一个类 { public: int id() const {return id_;} protected: ... }; //实现一个harsher,直接返回A::id() //实现一个equal,直接返回object1.id() == object2.id() std::unordered:set<A, harsher, equal> my_set; //根据id (100)查找对象: my_set.find(...); //如何写? [/quote] 你这个为什么不用 unordered_map 呢 ...[/quote] 只是讨论问题,对于你的提问,我只能回答:如果你unordered_set实现好了,能用set我为什么要用map呢?相比于用map,用set不会带来任何可读性、健壮性、代码量等等的损失,那我能省一点是一点啊(set比map省总是没问题的吧)。 而且最重要的一点,map是二维的,set是一维的,我有很多通用的一维窗口操作函数(直接操作*iterator),这些函数将统统不能用于操作map。[/quote] 不同的工具有不同的用处。 我们在用容器的时候,通常是根据我们需要的操作选择一个合适的容器。如果这个容器不合适,就换一个。如果 std 的容器不合适,但是 boost 的容器合适,那么用 boost 的也无妨。 但是容器设计的时候,是没有办法满足所有人的所有需求的。作为使用者,只能适应现有的容器,要不就自己写一个。 至于这个容器为啥这样设计,就得期待高人解释一下。 个人感觉,boost 那个接口,提供 keyhash 和 keyequal 也并不简单,并且要对这两个加很多约束才行。 比如 keyhash(key) == hash(value) 当且仅当 keyequal(key, value) == true 。 这有在某些特殊的情况下,提供这样两个函数才比较容易。你的这个就是个例子。[/quote] 那你就错了,unordered_set本来就需要那两个函数,你看看构造函数,unordered_map也一样,你未指定只是给你选择了一个默认的。那两个函数是不能少的,不管容不容易都得提供那两个函数。[/quote] unordered_set 需要的是 hash 和 equal 都是对 value 用的。 这里需要的另外的两个。 如果还用那两个的话,估计就还要构造对象。 [/quote] 来自:http://www.boost.org/doc/libs/1_62_0/doc/html/boost/unordered_set.html unordered_set lookup iterator find(key_type const& k); const_iterator find(key_type const& k) const; template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq); template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> const_iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const; Returns: An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. Notes: The templated overloads are a non-standard extensions which allows you to use a compatible hash function and equality predicate for a key of a different type in order to avoid an expensive type cast. In general, its use is not encouraged.[/quote] 这个我知道,这就是我的问题,为什么呢?boost的实现我觉得带来了很多好处,而且find有多个重载版本,你仍然可以使用以key为参数的查找,多一个功能何乐而不为呢?[/quote] 主要是很难明确清晰的界定相关类型间效果一致的 hash。 比如对于 unordered_set<unique_ptr<T*>>,标准提供 unique_ptr 的 hash,具体怎么做各家都可以不一样,你作为使用者看不到,并且也不应该看到。如果你需要用裸指针 T* 寻找 unordered map 里对应的 unique_ptr,通过 find 支持自定义 hash 实现,这个你自己提供的 hash,怎么保证对于同样的 T* 和 unique_ptr<T*> 一定能得到同样的 hash 值呢*(要求 unique_ptr 必须按裸指针 hash 可能是一种方法,但标准并不想把实现限制的这么死)?强行支持这种 find,基本没法写通用代码了,因为你提供的 hash 要想能够达到正确的效果,它的行为必然要保持和特定 stl unique_ptr 的 hash 的实现一致,最后代码就变成一坨 #ifdef。 诚然,对于主楼的用例,unordered_set 定义时的 hash 和 find 使用时的 hash 全是你控制,还算是在效果上可以保持一致。但从标准的角度讲,很难界定这两个不同的 hash 之间的行为。另外标准也不可能专门为你的特殊需求提供支持,要想进 stl 就必须具有普遍的适用性。[/quote] 另外,如果在一个unorderd_set存放复杂对象,提供hasher和equal 几乎是必须的,从这一点上来说,unordered_set只能选择相信用户提供的hasher和equal,那么既然在构造的时候可以提供hasher和equal,那在find的时候也可以提供,如果要说可能用户的hasher和equal有逻辑错误,那构造的时候的hasher和equal,unorderd_set还不是欣然接受了吗?[/quote] 根据我的理解,界定用户自定义 hash 的行为比较容易,标准已经做到了;界定两个 hash 产生等同的效果比较困难。你说的这种情况,应该属于前者的范畴,不能用此证明后者也是可行的。比如你主楼的用例,具体要求就是 KeyHasher(KeyType) == ContainerHasher(A.id) 当且仅当 KeyType 表示的东西(可以就是个 id,也可以是另一个重量级对对象,但我们只用它里面的某些字段组装个具有 id 功能的东西)等同于 A.id 的时候。这里面模糊信息太多,自由度太大,目前标准还没想出好方法用清晰明了的技术语言表述之,并且保证各编译器厂商能够准确无误的实现统一行为。话说回来,你要是有好的方法,完全可以写提案,然后申请扩充或修改标准。其实 std::set/map 的 find 本来面临类似的问题,不过那边只需要定义 < 和 std::less/greater 等等的,容易多了,所以最后特化一下 std::less<void> 就给解决了。我很期待有高人能把 unordered 的 find 问题也解决了。楼主加油哦。[/quote] 如果眼睛里容不得一点沙子的话,这个问题恐怕无解,但至少有人跟我一样有一些不爽,比如boost::container的作者。 [/quote] boost 文档里说 find is not encouraged. 我猜是有人不爽,然后欠佳思考的就加了个重载,使用过程中结果遇到一堆问题,带来的问题比解决的还多,但是已经加进去了,为了兼容历史,一时又撤不掉,所以只好说 not encouraged 的,有点类似于标准的 deprecated。从这点说标准保守的做法反而更好。
ri_aje 2016-11-18
  • 打赏
  • 举报
回复
引用 21 楼 yang79tao 的回复:
[quote=引用 19 楼 pengzhixi 的回复:] 通用性优先。至于你所说的困惑,如果你单单限定使用unorder_set那只能是说你自己不够灵活。你想把Key的比较简单化,不涉及真正的value比较 标准库完全有其他容器达到你的需求。
如果在一个unorderd_set存放复杂对象,提供hasher和equal 几乎是必须的
引用 18 楼 ri_aje 的回复:
[quote=引用 16 楼 yang79tao 的回复:] [quote=引用 15 楼 fefe82 的回复:] [quote=引用 14 楼 fefe82 的回复:] [quote=引用 13 楼 yang79tao 的回复:] [quote=引用 12 楼 fefe82 的回复:] [quote=引用 7 楼 yang79tao 的回复:] [quote=引用 5 楼 fefe82 的回复:] [quote=引用 3 楼 yang79tao 的回复:] [quote=引用 1 楼 paschen 的回复:] std::unordered_set 的 key 就是value std::unordered_map 本来就是以key来查找value而设计,如果要以value来查找,就需要一个遍历所有元素,一个个比较
你要是遇到我的难处就知道我的意思了,举例: class A //非常复杂一个类 { public: int id() const {return id_;} protected: ... }; //实现一个harsher,直接返回A::id() //实现一个equal,直接返回object1.id() == object2.id() std::unordered:set<A, harsher, equal> my_set; //根据id (100)查找对象: my_set.find(...); //如何写? [/quote] 你这个为什么不用 unordered_map 呢 ...[/quote] 只是讨论问题,对于你的提问,我只能回答:如果你unordered_set实现好了,能用set我为什么要用map呢?相比于用map,用set不会带来任何可读性、健壮性、代码量等等的损失,那我能省一点是一点啊(set比map省总是没问题的吧)。 而且最重要的一点,map是二维的,set是一维的,我有很多通用的一维窗口操作函数(直接操作*iterator),这些函数将统统不能用于操作map。[/quote] 不同的工具有不同的用处。 我们在用容器的时候,通常是根据我们需要的操作选择一个合适的容器。如果这个容器不合适,就换一个。如果 std 的容器不合适,但是 boost 的容器合适,那么用 boost 的也无妨。 但是容器设计的时候,是没有办法满足所有人的所有需求的。作为使用者,只能适应现有的容器,要不就自己写一个。 至于这个容器为啥这样设计,就得期待高人解释一下。 个人感觉,boost 那个接口,提供 keyhash 和 keyequal 也并不简单,并且要对这两个加很多约束才行。 比如 keyhash(key) == hash(value) 当且仅当 keyequal(key, value) == true 。 这有在某些特殊的情况下,提供这样两个函数才比较容易。你的这个就是个例子。[/quote] 那你就错了,unordered_set本来就需要那两个函数,你看看构造函数,unordered_map也一样,你未指定只是给你选择了一个默认的。那两个函数是不能少的,不管容不容易都得提供那两个函数。[/quote] unordered_set 需要的是 hash 和 equal 都是对 value 用的。 这里需要的另外的两个。 如果还用那两个的话,估计就还要构造对象。 [/quote] 来自:http://www.boost.org/doc/libs/1_62_0/doc/html/boost/unordered_set.html unordered_set lookup iterator find(key_type const& k); const_iterator find(key_type const& k) const; template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq); template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> const_iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const; Returns: An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. Notes: The templated overloads are a non-standard extensions which allows you to use a compatible hash function and equality predicate for a key of a different type in order to avoid an expensive type cast. In general, its use is not encouraged.[/quote] 这个我知道,这就是我的问题,为什么呢?boost的实现我觉得带来了很多好处,而且find有多个重载版本,你仍然可以使用以key为参数的查找,多一个功能何乐而不为呢?[/quote] 主要是很难明确清晰的界定相关类型间效果一致的 hash。 比如对于 unordered_set<unique_ptr<T*>>,标准提供 unique_ptr 的 hash,具体怎么做各家都可以不一样,你作为使用者看不到,并且也不应该看到。如果你需要用裸指针 T* 寻找 unordered map 里对应的 unique_ptr,通过 find 支持自定义 hash 实现,这个你自己提供的 hash,怎么保证对于同样的 T* 和 unique_ptr<T*> 一定能得到同样的 hash 值呢*(要求 unique_ptr 必须按裸指针 hash 可能是一种方法,但标准并不想把实现限制的这么死)?强行支持这种 find,基本没法写通用代码了,因为你提供的 hash 要想能够达到正确的效果,它的行为必然要保持和特定 stl unique_ptr 的 hash 的实现一致,最后代码就变成一坨 #ifdef。 诚然,对于主楼的用例,unordered_set 定义时的 hash 和 find 使用时的 hash 全是你控制,还算是在效果上可以保持一致。但从标准的角度讲,很难界定这两个不同的 hash 之间的行为。另外标准也不可能专门为你的特殊需求提供支持,要想进 stl 就必须具有普遍的适用性。[/quote] 另外,如果在一个unorderd_set存放复杂对象,提供hasher和equal 几乎是必须的,从这一点上来说,unordered_set只能选择相信用户提供的hasher和equal,那么既然在构造的时候可以提供hasher和equal,那在find的时候也可以提供,如果要说可能用户的hasher和equal有逻辑错误,那构造的时候的hasher和equal,unorderd_set还不是欣然接受了吗?[/quote] 根据我的理解,界定用户自定义 hash 的行为比较容易,标准已经做到了;界定两个 hash 产生等同的效果比较困难。你说的这种情况,应该属于前者的范畴,不能用此证明后者也是可行的。比如你主楼的用例,具体要求就是 KeyHasher(KeyType) == ContainerHasher(A.id) 当且仅当 KeyType 表示的东西(可以就是个 id,也可以是另一个重量级对对象,但我们只用它里面的某些字段组装个具有 id 功能的东西)等同于 A.id 的时候。这里面模糊信息太多,自由度太大,目前标准还没想出好方法用清晰明了的技术语言表述之,并且保证各编译器厂商能够准确无误的实现统一行为。话说回来,你要是有好的方法,完全可以写提案,然后申请扩充或修改标准。其实 std::set/map 的 find 本来面临类似的问题,不过那边只需要定义 < 和 std::less/greater 等等的,容易多了,所以最后特化一下 std::less<void> 就给解决了。我很期待有高人能把 unordered 的 find 问题也解决了。楼主加油哦。
  • 打赏
  • 举报
回复
引用 22 楼 yang79tao 的回复:
关于使用unordered_set的好处,除了减少内存占用,操作起来也更直接,比如以我的一个实际例子为例: template<typename _Can, typename _Predicate> void do_something_to_all(_Can& __can, const _Predicate& __pred) {for (auto& item : __can) __pred(item);} 这个函数是对一个容器里面的所有项调用__pred,这个函数支持所有容器,除了类map容器。 为了支持类map容器,我不得不写另外一个函数,可是叫什么名字呢,do_something_to_all_2,这一看就是迫不得已。
重载一下呗,针对参数是pair<xxx,xxx>
pengzhixi 2016-11-18
  • 打赏
  • 举报
回复
标准对这些容器不是没提供hasher只是没有一个放任四海(对所有类型)都适用的hasher。至于equal也是一样。所以对于user-define的类型 提供hasher和equal这个不是应该的么?
fefe82 2016-11-18
  • 打赏
  • 举报
回复
std::for_each(iter1, iter2, pred); std::for_each(map_iter1, map_iter2, [](map_value_type &v){pred(v.second);});
youngwolf 2016-11-18
  • 打赏
  • 举报
回复
关于使用unordered_set的好处,除了减少内存占用,操作起来也更直接,比如以我的一个实际例子为例: template<typename _Can, typename _Predicate> void do_something_to_all(_Can& __can, const _Predicate& __pred) {for (auto& item : __can) __pred(item);} 这个函数是对一个容器里面的所有项调用__pred,这个函数支持所有容器,除了类map容器。 为了支持类map容器,我不得不写另外一个函数,可是叫什么名字呢,do_something_to_all_2,这一看就是迫不得已。
youngwolf 2016-11-18
  • 打赏
  • 举报
回复
引用 19 楼 pengzhixi 的回复:
通用性优先。至于你所说的困惑,如果你单单限定使用unorder_set那只能是说你自己不够灵活。你想把Key的比较简单化,不涉及真正的value比较 标准库完全有其他容器达到你的需求。
如果在一个unorderd_set存放复杂对象,提供hasher和equal 几乎是必须的
引用 18 楼 ri_aje 的回复:
[quote=引用 16 楼 yang79tao 的回复:] [quote=引用 15 楼 fefe82 的回复:] [quote=引用 14 楼 fefe82 的回复:] [quote=引用 13 楼 yang79tao 的回复:] [quote=引用 12 楼 fefe82 的回复:] [quote=引用 7 楼 yang79tao 的回复:] [quote=引用 5 楼 fefe82 的回复:] [quote=引用 3 楼 yang79tao 的回复:] [quote=引用 1 楼 paschen 的回复:] std::unordered_set 的 key 就是value std::unordered_map 本来就是以key来查找value而设计,如果要以value来查找,就需要一个遍历所有元素,一个个比较
你要是遇到我的难处就知道我的意思了,举例: class A //非常复杂一个类 { public: int id() const {return id_;} protected: ... }; //实现一个harsher,直接返回A::id() //实现一个equal,直接返回object1.id() == object2.id() std::unordered:set<A, harsher, equal> my_set; //根据id (100)查找对象: my_set.find(...); //如何写? [/quote] 你这个为什么不用 unordered_map 呢 ...[/quote] 只是讨论问题,对于你的提问,我只能回答:如果你unordered_set实现好了,能用set我为什么要用map呢?相比于用map,用set不会带来任何可读性、健壮性、代码量等等的损失,那我能省一点是一点啊(set比map省总是没问题的吧)。 而且最重要的一点,map是二维的,set是一维的,我有很多通用的一维窗口操作函数(直接操作*iterator),这些函数将统统不能用于操作map。[/quote] 不同的工具有不同的用处。 我们在用容器的时候,通常是根据我们需要的操作选择一个合适的容器。如果这个容器不合适,就换一个。如果 std 的容器不合适,但是 boost 的容器合适,那么用 boost 的也无妨。 但是容器设计的时候,是没有办法满足所有人的所有需求的。作为使用者,只能适应现有的容器,要不就自己写一个。 至于这个容器为啥这样设计,就得期待高人解释一下。 个人感觉,boost 那个接口,提供 keyhash 和 keyequal 也并不简单,并且要对这两个加很多约束才行。 比如 keyhash(key) == hash(value) 当且仅当 keyequal(key, value) == true 。 这有在某些特殊的情况下,提供这样两个函数才比较容易。你的这个就是个例子。[/quote] 那你就错了,unordered_set本来就需要那两个函数,你看看构造函数,unordered_map也一样,你未指定只是给你选择了一个默认的。那两个函数是不能少的,不管容不容易都得提供那两个函数。[/quote] unordered_set 需要的是 hash 和 equal 都是对 value 用的。 这里需要的另外的两个。 如果还用那两个的话,估计就还要构造对象。 [/quote] 来自:http://www.boost.org/doc/libs/1_62_0/doc/html/boost/unordered_set.html unordered_set lookup iterator find(key_type const& k); const_iterator find(key_type const& k) const; template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq); template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> const_iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const; Returns: An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. Notes: The templated overloads are a non-standard extensions which allows you to use a compatible hash function and equality predicate for a key of a different type in order to avoid an expensive type cast. In general, its use is not encouraged.[/quote] 这个我知道,这就是我的问题,为什么呢?boost的实现我觉得带来了很多好处,而且find有多个重载版本,你仍然可以使用以key为参数的查找,多一个功能何乐而不为呢?[/quote] 主要是很难明确清晰的界定相关类型间效果一致的 hash。 比如对于 unordered_set<unique_ptr<T*>>,标准提供 unique_ptr 的 hash,具体怎么做各家都可以不一样,你作为使用者看不到,并且也不应该看到。如果你需要用裸指针 T* 寻找 unordered map 里对应的 unique_ptr,通过 find 支持自定义 hash 实现,这个你自己提供的 hash,怎么保证对于同样的 T* 和 unique_ptr<T*> 一定能得到同样的 hash 值呢*(要求 unique_ptr 必须按裸指针 hash 可能是一种方法,但标准并不想把实现限制的这么死)?强行支持这种 find,基本没法写通用代码了,因为你提供的 hash 要想能够达到正确的效果,它的行为必然要保持和特定 stl unique_ptr 的 hash 的实现一致,最后代码就变成一坨 #ifdef。 诚然,对于主楼的用例,unordered_set 定义时的 hash 和 find 使用时的 hash 全是你控制,还算是在效果上可以保持一致。但从标准的角度讲,很难界定这两个不同的 hash 之间的行为。另外标准也不可能专门为你的特殊需求提供支持,要想进 stl 就必须具有普遍的适用性。[/quote] 另外,如果在一个unorderd_set存放复杂对象,提供hasher和equal 几乎是必须的,从这一点上来说,unordered_set只能选择相信用户提供的hasher和equal,那么既然在构造的时候可以提供hasher和equal,那在find的时候也可以提供,如果要说可能用户的hasher和equal有逻辑错误,那构造的时候的hasher和equal,unorderd_set还不是欣然接受了吗?
youngwolf 2016-11-18
  • 打赏
  • 举报
回复
引用 18 楼 ri_aje 的回复:
[quote=引用 16 楼 yang79tao 的回复:] [quote=引用 15 楼 fefe82 的回复:] [quote=引用 14 楼 fefe82 的回复:] [quote=引用 13 楼 yang79tao 的回复:] [quote=引用 12 楼 fefe82 的回复:] [quote=引用 7 楼 yang79tao 的回复:] [quote=引用 5 楼 fefe82 的回复:] [quote=引用 3 楼 yang79tao 的回复:] [quote=引用 1 楼 paschen 的回复:] std::unordered_set 的 key 就是value std::unordered_map 本来就是以key来查找value而设计,如果要以value来查找,就需要一个遍历所有元素,一个个比较
你要是遇到我的难处就知道我的意思了,举例: class A //非常复杂一个类 { public: int id() const {return id_;} protected: ... }; //实现一个harsher,直接返回A::id() //实现一个equal,直接返回object1.id() == object2.id() std::unordered:set<A, harsher, equal> my_set; //根据id (100)查找对象: my_set.find(...); //如何写? [/quote] 你这个为什么不用 unordered_map 呢 ...[/quote] 只是讨论问题,对于你的提问,我只能回答:如果你unordered_set实现好了,能用set我为什么要用map呢?相比于用map,用set不会带来任何可读性、健壮性、代码量等等的损失,那我能省一点是一点啊(set比map省总是没问题的吧)。 而且最重要的一点,map是二维的,set是一维的,我有很多通用的一维窗口操作函数(直接操作*iterator),这些函数将统统不能用于操作map。[/quote] 不同的工具有不同的用处。 我们在用容器的时候,通常是根据我们需要的操作选择一个合适的容器。如果这个容器不合适,就换一个。如果 std 的容器不合适,但是 boost 的容器合适,那么用 boost 的也无妨。 但是容器设计的时候,是没有办法满足所有人的所有需求的。作为使用者,只能适应现有的容器,要不就自己写一个。 至于这个容器为啥这样设计,就得期待高人解释一下。 个人感觉,boost 那个接口,提供 keyhash 和 keyequal 也并不简单,并且要对这两个加很多约束才行。 比如 keyhash(key) == hash(value) 当且仅当 keyequal(key, value) == true 。 这有在某些特殊的情况下,提供这样两个函数才比较容易。你的这个就是个例子。[/quote] 那你就错了,unordered_set本来就需要那两个函数,你看看构造函数,unordered_map也一样,你未指定只是给你选择了一个默认的。那两个函数是不能少的,不管容不容易都得提供那两个函数。[/quote] unordered_set 需要的是 hash 和 equal 都是对 value 用的。 这里需要的另外的两个。 如果还用那两个的话,估计就还要构造对象。 [/quote] 来自:http://www.boost.org/doc/libs/1_62_0/doc/html/boost/unordered_set.html unordered_set lookup iterator find(key_type const& k); const_iterator find(key_type const& k) const; template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq); template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> const_iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const; Returns: An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. Notes: The templated overloads are a non-standard extensions which allows you to use a compatible hash function and equality predicate for a key of a different type in order to avoid an expensive type cast. In general, its use is not encouraged.[/quote] 这个我知道,这就是我的问题,为什么呢?boost的实现我觉得带来了很多好处,而且find有多个重载版本,你仍然可以使用以key为参数的查找,多一个功能何乐而不为呢?[/quote] 主要是很难明确清晰的界定相关类型间效果一致的 hash。 比如对于 unordered_set<unique_ptr<T*>>,标准提供 unique_ptr 的 hash,具体怎么做各家都可以不一样,你作为使用者看不到,并且也不应该看到。如果你需要用裸指针 T* 寻找 unordered map 里对应的 unique_ptr,通过 find 支持自定义 hash 实现,这个你自己提供的 hash,怎么保证对于同样的 T* 和 unique_ptr<T*> 一定能得到同样的 hash 值呢*(要求 unique_ptr 必须按裸指针 hash 可能是一种方法,但标准并不想把实现限制的这么死)?强行支持这种 find,基本没法写通用代码了,因为你提供的 hash 要想能够达到正确的效果,它的行为必然要保持和特定 stl unique_ptr 的 hash 的实现一致,最后代码就变成一坨 #ifdef。 诚然,对于主楼的用例,unordered_set 定义时的 hash 和 find 使用时的 hash 全是你控制,还算是在效果上可以保持一致。但从标准的角度讲,很难界定这两个不同的 hash 之间的行为。另外标准也不可能专门为你的特殊需求提供支持,要想进 stl 就必须具有普遍的适用性。[/quote] 这一点我想过,算得上是一个理由。即使我在构造unordered_set的时候,和在find的时候,都自定义了hasher和equal函数,也有可能出现逻辑错误,因为它们是两套hasher和equal。
pengzhixi 2016-11-18
  • 打赏
  • 举报
回复
通用性优先。至于你所说的困惑,如果你单单限定使用unorder_set那只能是说你自己不够灵活。你想把Key的比较简单化,不涉及真正的value比较 标准库完全有其他容器达到你的需求。
ID870177103 2016-11-18
  • 打赏
  • 举报
回复
map是一种数据结构,这种数据结构设计来是为了,用key来快速找到value 至于为什么不提供findkey的方法,你可以考虑一下为什么vector不提供元素之间的swap方法 另外unordered map几乎肯定比ordered map更消耗内存,因为哈希查找就是一种空间换时间的方法 如果你需要动态重绑定这类模板的参数,可以试试下面的方法
struct HASHCODE {
	size_t operator() (const int &a) const {
		cout << "called" << endl ;
		return 0 ;
	}
} ;


int main () {
	std::unordered_map<int ,int> a ;
	reinterpret_cast<std::unordered_map<int ,int ,HASHCODE> &> (a).find (0) ;
	system ("pause") ;
	return 0 ;
}
  • 打赏
  • 举报
回复
引用 28 楼 yang79tao 的回复:
这能重载吗?外面的代码已经写好了,比如使用代码是: do_something_to_all([](const auto& item) {item->show_info("", "");}); 要如何重载do_something_to_all才能让上面的代码调用到新的do_something_to_all?
这样写重载版不就行了嘛

template<typename K, typename V, typename _Predicate>
void do_something_to_all( std::map<K, V>& __can, const _Predicate& __pred )
{
    for ( auto& item : __can ) __pred( item.second );
}
完整测试代码
#include <vector>
#include <map>
#include<stdio.h>
using namespace std;


struct test
{
    int value;
    test( int i = 0 ): value( i ) {}
    void show_info( const char*, const char* )const
    {
        printf( "%d\n", value );
    }
};

template<typename _Can, typename _Predicate>
void do_something_to_all( _Can& __can, const _Predicate& __pred )
{
    for ( auto& item : __can ) __pred( item );
}

template<typename K, typename V, typename _Predicate>
void do_something_to_all( std::map<K, V>& __can, const _Predicate& __pred )
{
    for ( auto& item : __can ) __pred( item.second );
}

int main( int argc, char const *argv[] )
{
	auto func =  []( const auto & item )
    {
        item.show_info( "", "" );
    } ;
	
    std::map<int, test> m1;
    m1[1] = test( 111 );
    m1[9] = test( 99 );
    do_something_to_all( m1,func );

    std::vector<test> v1;
    v1.push_back( test( 222 ) );
    v1.push_back( test( 333 ) );
    do_something_to_all( v1, func );
    return 0;
}
youngwolf 2016-11-18
  • 打赏
  • 举报
回复
引用 26 楼 ri_aje 的回复:
[quote=引用 21 楼 yang79tao 的回复:] [quote=引用 19 楼 pengzhixi 的回复:] 通用性优先。至于你所说的困惑,如果你单单限定使用unorder_set那只能是说你自己不够灵活。你想把Key的比较简单化,不涉及真正的value比较 标准库完全有其他容器达到你的需求。
如果在一个unorderd_set存放复杂对象,提供hasher和equal 几乎是必须的
引用 18 楼 ri_aje 的回复:
[quote=引用 16 楼 yang79tao 的回复:] [quote=引用 15 楼 fefe82 的回复:] [quote=引用 14 楼 fefe82 的回复:] [quote=引用 13 楼 yang79tao 的回复:] [quote=引用 12 楼 fefe82 的回复:] [quote=引用 7 楼 yang79tao 的回复:] [quote=引用 5 楼 fefe82 的回复:] [quote=引用 3 楼 yang79tao 的回复:] [quote=引用 1 楼 paschen 的回复:] std::unordered_set 的 key 就是value std::unordered_map 本来就是以key来查找value而设计,如果要以value来查找,就需要一个遍历所有元素,一个个比较
你要是遇到我的难处就知道我的意思了,举例: class A //非常复杂一个类 { public: int id() const {return id_;} protected: ... }; //实现一个harsher,直接返回A::id() //实现一个equal,直接返回object1.id() == object2.id() std::unordered:set<A, harsher, equal> my_set; //根据id (100)查找对象: my_set.find(...); //如何写? [/quote] 你这个为什么不用 unordered_map 呢 ...[/quote] 只是讨论问题,对于你的提问,我只能回答:如果你unordered_set实现好了,能用set我为什么要用map呢?相比于用map,用set不会带来任何可读性、健壮性、代码量等等的损失,那我能省一点是一点啊(set比map省总是没问题的吧)。 而且最重要的一点,map是二维的,set是一维的,我有很多通用的一维窗口操作函数(直接操作*iterator),这些函数将统统不能用于操作map。[/quote] 不同的工具有不同的用处。 我们在用容器的时候,通常是根据我们需要的操作选择一个合适的容器。如果这个容器不合适,就换一个。如果 std 的容器不合适,但是 boost 的容器合适,那么用 boost 的也无妨。 但是容器设计的时候,是没有办法满足所有人的所有需求的。作为使用者,只能适应现有的容器,要不就自己写一个。 至于这个容器为啥这样设计,就得期待高人解释一下。 个人感觉,boost 那个接口,提供 keyhash 和 keyequal 也并不简单,并且要对这两个加很多约束才行。 比如 keyhash(key) == hash(value) 当且仅当 keyequal(key, value) == true 。 这有在某些特殊的情况下,提供这样两个函数才比较容易。你的这个就是个例子。[/quote] 那你就错了,unordered_set本来就需要那两个函数,你看看构造函数,unordered_map也一样,你未指定只是给你选择了一个默认的。那两个函数是不能少的,不管容不容易都得提供那两个函数。[/quote] unordered_set 需要的是 hash 和 equal 都是对 value 用的。 这里需要的另外的两个。 如果还用那两个的话,估计就还要构造对象。 [/quote] 来自:http://www.boost.org/doc/libs/1_62_0/doc/html/boost/unordered_set.html unordered_set lookup iterator find(key_type const& k); const_iterator find(key_type const& k) const; template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq); template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> const_iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const; Returns: An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. Notes: The templated overloads are a non-standard extensions which allows you to use a compatible hash function and equality predicate for a key of a different type in order to avoid an expensive type cast. In general, its use is not encouraged.[/quote] 这个我知道,这就是我的问题,为什么呢?boost的实现我觉得带来了很多好处,而且find有多个重载版本,你仍然可以使用以key为参数的查找,多一个功能何乐而不为呢?[/quote] 主要是很难明确清晰的界定相关类型间效果一致的 hash。 比如对于 unordered_set<unique_ptr<T*>>,标准提供 unique_ptr 的 hash,具体怎么做各家都可以不一样,你作为使用者看不到,并且也不应该看到。如果你需要用裸指针 T* 寻找 unordered map 里对应的 unique_ptr,通过 find 支持自定义 hash 实现,这个你自己提供的 hash,怎么保证对于同样的 T* 和 unique_ptr<T*> 一定能得到同样的 hash 值呢*(要求 unique_ptr 必须按裸指针 hash 可能是一种方法,但标准并不想把实现限制的这么死)?强行支持这种 find,基本没法写通用代码了,因为你提供的 hash 要想能够达到正确的效果,它的行为必然要保持和特定 stl unique_ptr 的 hash 的实现一致,最后代码就变成一坨 #ifdef。 诚然,对于主楼的用例,unordered_set 定义时的 hash 和 find 使用时的 hash 全是你控制,还算是在效果上可以保持一致。但从标准的角度讲,很难界定这两个不同的 hash 之间的行为。另外标准也不可能专门为你的特殊需求提供支持,要想进 stl 就必须具有普遍的适用性。[/quote] 另外,如果在一个unorderd_set存放复杂对象,提供hasher和equal 几乎是必须的,从这一点上来说,unordered_set只能选择相信用户提供的hasher和equal,那么既然在构造的时候可以提供hasher和equal,那在find的时候也可以提供,如果要说可能用户的hasher和equal有逻辑错误,那构造的时候的hasher和equal,unorderd_set还不是欣然接受了吗?[/quote] 根据我的理解,界定用户自定义 hash 的行为比较容易,标准已经做到了;界定两个 hash 产生等同的效果比较困难。你说的这种情况,应该属于前者的范畴,不能用此证明后者也是可行的。比如你主楼的用例,具体要求就是 KeyHasher(KeyType) == ContainerHasher(A.id) 当且仅当 KeyType 表示的东西(可以就是个 id,也可以是另一个重量级对对象,但我们只用它里面的某些字段组装个具有 id 功能的东西)等同于 A.id 的时候。这里面模糊信息太多,自由度太大,目前标准还没想出好方法用清晰明了的技术语言表述之,并且保证各编译器厂商能够准确无误的实现统一行为。话说回来,你要是有好的方法,完全可以写提案,然后申请扩充或修改标准。其实 std::set/map 的 find 本来面临类似的问题,不过那边只需要定义 < 和 std::less/greater 等等的,容易多了,所以最后特化一下 std::less<void> 就给解决了。我很期待有高人能把 unordered 的 find 问题也解决了。楼主加油哦。[/quote] 如果眼睛里容不得一点沙子的话,这个问题恐怕无解,但至少有人跟我一样有一些不爽,比如boost::container的作者。
youngwolf 2016-11-18
  • 打赏
  • 举报
回复
引用 25 楼 akirya 的回复:
[quote=引用 22 楼 yang79tao 的回复:] 关于使用unordered_set的好处,除了减少内存占用,操作起来也更直接,比如以我的一个实际例子为例: template<typename _Can, typename _Predicate> void do_something_to_all(_Can& __can, const _Predicate& __pred) {for (auto& item : __can) __pred(item);} 这个函数是对一个容器里面的所有项调用__pred,这个函数支持所有容器,除了类map容器。 为了支持类map容器,我不得不写另外一个函数,可是叫什么名字呢,do_something_to_all_2,这一看就是迫不得已。
重载一下呗,针对参数是pair<xxx,xxx>[/quote]
引用 25 楼 akirya 的回复:
[quote=引用 22 楼 yang79tao 的回复:] 关于使用unordered_set的好处,除了减少内存占用,操作起来也更直接,比如以我的一个实际例子为例: template<typename _Can, typename _Predicate> void do_something_to_all(_Can& __can, const _Predicate& __pred) {for (auto& item : __can) __pred(item);} 这个函数是对一个容器里面的所有项调用__pred,这个函数支持所有容器,除了类map容器。 为了支持类map容器,我不得不写另外一个函数,可是叫什么名字呢,do_something_to_all_2,这一看就是迫不得已。
重载一下呗,针对参数是pair<xxx,xxx>[/quote] 这能重载吗?外面的代码已经写好了,比如使用代码是: do_something_to_all([](const auto& item) {item->show_info("", "");}); 要如何重载do_something_to_all才能让上面的代码调用到新的do_something_to_all?
ri_aje 2016-11-18
  • 打赏
  • 举报
回复
引用 16 楼 yang79tao 的回复:
[quote=引用 15 楼 fefe82 的回复:] [quote=引用 14 楼 fefe82 的回复:] [quote=引用 13 楼 yang79tao 的回复:] [quote=引用 12 楼 fefe82 的回复:] [quote=引用 7 楼 yang79tao 的回复:] [quote=引用 5 楼 fefe82 的回复:] [quote=引用 3 楼 yang79tao 的回复:] [quote=引用 1 楼 paschen 的回复:] std::unordered_set 的 key 就是value std::unordered_map 本来就是以key来查找value而设计,如果要以value来查找,就需要一个遍历所有元素,一个个比较
你要是遇到我的难处就知道我的意思了,举例: class A //非常复杂一个类 { public: int id() const {return id_;} protected: ... }; //实现一个harsher,直接返回A::id() //实现一个equal,直接返回object1.id() == object2.id() std::unordered:set<A, harsher, equal> my_set; //根据id (100)查找对象: my_set.find(...); //如何写? [/quote] 你这个为什么不用 unordered_map 呢 ...[/quote] 只是讨论问题,对于你的提问,我只能回答:如果你unordered_set实现好了,能用set我为什么要用map呢?相比于用map,用set不会带来任何可读性、健壮性、代码量等等的损失,那我能省一点是一点啊(set比map省总是没问题的吧)。 而且最重要的一点,map是二维的,set是一维的,我有很多通用的一维窗口操作函数(直接操作*iterator),这些函数将统统不能用于操作map。[/quote] 不同的工具有不同的用处。 我们在用容器的时候,通常是根据我们需要的操作选择一个合适的容器。如果这个容器不合适,就换一个。如果 std 的容器不合适,但是 boost 的容器合适,那么用 boost 的也无妨。 但是容器设计的时候,是没有办法满足所有人的所有需求的。作为使用者,只能适应现有的容器,要不就自己写一个。 至于这个容器为啥这样设计,就得期待高人解释一下。 个人感觉,boost 那个接口,提供 keyhash 和 keyequal 也并不简单,并且要对这两个加很多约束才行。 比如 keyhash(key) == hash(value) 当且仅当 keyequal(key, value) == true 。 这有在某些特殊的情况下,提供这样两个函数才比较容易。你的这个就是个例子。[/quote] 那你就错了,unordered_set本来就需要那两个函数,你看看构造函数,unordered_map也一样,你未指定只是给你选择了一个默认的。那两个函数是不能少的,不管容不容易都得提供那两个函数。[/quote] unordered_set 需要的是 hash 和 equal 都是对 value 用的。 这里需要的另外的两个。 如果还用那两个的话,估计就还要构造对象。 [/quote] 来自:http://www.boost.org/doc/libs/1_62_0/doc/html/boost/unordered_set.html unordered_set lookup iterator find(key_type const& k); const_iterator find(key_type const& k) const; template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq); template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> const_iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const; Returns: An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. Notes: The templated overloads are a non-standard extensions which allows you to use a compatible hash function and equality predicate for a key of a different type in order to avoid an expensive type cast. In general, its use is not encouraged.[/quote] 这个我知道,这就是我的问题,为什么呢?boost的实现我觉得带来了很多好处,而且find有多个重载版本,你仍然可以使用以key为参数的查找,多一个功能何乐而不为呢?[/quote] 主要是很难明确清晰的界定相关类型间效果一致的 hash。 比如对于 unordered_set<unique_ptr<T*>>,标准提供 unique_ptr 的 hash,具体怎么做各家都可以不一样,你作为使用者看不到,并且也不应该看到。如果你需要用裸指针 T* 寻找 unordered map 里对应的 unique_ptr,通过 find 支持自定义 hash 实现,这个你自己提供的 hash,怎么保证对于同样的 T* 和 unique_ptr<T*> 一定能得到同样的 hash 值呢*(要求 unique_ptr 必须按裸指针 hash 可能是一种方法,但标准并不想把实现限制的这么死)?强行支持这种 find,基本没法写通用代码了,因为你提供的 hash 要想能够达到正确的效果,它的行为必然要保持和特定 stl unique_ptr 的 hash 的实现一致,最后代码就变成一坨 #ifdef。 诚然,对于主楼的用例,unordered_set 定义时的 hash 和 find 使用时的 hash 全是你控制,还算是在效果上可以保持一致。但从标准的角度讲,很难界定这两个不同的 hash 之间的行为。另外标准也不可能专门为你的特殊需求提供支持,要想进 stl 就必须具有普遍的适用性。
赵4老师 2016-11-18
  • 打赏
  • 举报
回复
加一分嫌多,减一分嫌少。 这才是堪称完美的设计。 比如筷子。
  • 打赏
  • 举报
回复
引用 16 楼 yang79tao 的回复:
这个我知道,这就是我的问题,为什么呢?boost的实现我觉得带来了很多好处,而且find有多个重载版本,你仍然可以使用以key为参数的查找,多一个功能何乐而不为呢?
1 不够简单 2 有简单的替代方案
加载更多回复(16)

65,187

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧