我想知道是否可以像这样创建td
类型的变量:
std::unordered_map
我希望它能够正常工作,因为std::unordered_map<std::weak_ptr<A>, B, std::hash<std::weak_ptr<A>,
[](const std::weak_ptr<A>& lhs, const std::weak_ptr<B>& rhs) -> bool { return lhs.lock() == rhs.lock(); }>
模板只需要一个实现KeyEqual
运算符的类型,但是visual studio不会让我编译它,说它缺少一个类型, lambda代表。
答案 0 :(得分:2)
您必须在构造函数中定义lambda实现。 有一些例子如何做到这一点。
auto hash = std::unordered_map<std::string, int, std::hash<std::string>, std::function<bool(const std::string&, const std::string&) >>(0, std::hash<std::string>(),
[](const std::string& lhs, const std::string& rhs)
{
return lhs == rhs;
}
);
答案 1 :(得分:2)
首先,正如Richard Hodges在答案中所说,你不能使用std::weak_ptr
作为关键,因为它不稳定。忽略这一点,我们可以看一般问题:我们可以将lambdas用于模板参数。
通用解决方案是按照the following answer中的描述进行的。有两件事需要注意
2)的原因是从编译器为lambda创建的类型中删除了默认构造函数。
对于std::set
这并不坏,但考虑std::unordered_map
没有构造函数采用单个键比较函数:
auto compare = [](const A & lhs, const A & rhs) { return lhs==rhs; };
std::unordered_map<
A,
B,
std::hash<A>,
decltype(compare)
> map1{0, std::hash<A>{}, compare};
第一个参数是初始存储桶大小,并且是实现定义的。我假设在插入第一个项目时实现将找到优化值,我使用0。第二个是哈希函数,最后是lambda。
我们可以将decltype(...)
替换为function<bool(A,A)>
来改善它。这允许我们在头中声明类型,因此将其传递给其他函数,而不需要共享实际的lambda。声明将成为:
typedef std::unordered_map<
A,
B,
std::hash<A>,
std::function<bool(A,A)>
> custom_unordered_map;
可以按如下方式使用:
custom_unordered_map map2{0, std::hash<A>{},
[](const A & lhs, const A & rhs) { return lhs==rhs; } };
此解决方案允许直接使用自定义lambda,也可以使用自由函数。
此解决方案的主要优点是它允许使用不同的比较功能,但使用起来非常冗长。
如果只需要一个比较函数,那么一个不太详细的解决方案(对于该类型的用户)就是定义一个仿函数:
struct Compare {
bool operator () (const A & lhs, const A & rhs) {
return lhs==rhs;
}
};
然后可以以正常方式使用:
std::unordered_map<A, B, std::hash<A>, Compare> map4;
注意:使用此解决方案,可以使用默认构造函数。
以下是一个完整的例子:
#include <functional>
#include <memory>
#include <unordered_map>
using A = int;
using B = int;
struct Compare {
bool operator () (const A & lhs, const A & rhs) {
return lhs==rhs;
}
};
bool compare_func(const A & lhs, const A & rhs) {
return lhs==rhs;
}
int main() {
// Using lamda: default constructor is deleted, so the lambda
// must be passed as argument to the constructor.
auto compare = [](const A & lhs, const A & rhs) { return lhs==rhs; };
std::unordered_map<
A,
B,
std::hash<A>,
decltype(compare)
> map1{0, std::hash<A>{}, compare};
// Alternative: use std::function. More general, and allows any lambda to be used
typedef std::unordered_map<
A,
B,
std::hash<A>,
std::function<bool(A,A)>
> custom_unordered_map;
custom_unordered_map map2{0, std::hash<A>{},
[](const A & lhs, const A & rhs) { return lhs==rhs; } };
custom_unordered_map map3{0, std::hash<A>{}, compare_func};
// Use of function class
std::unordered_map<A, B, std::hash<A>, Compare> map4;
}
这可以使用命令g++ map_lambda.cpp --std=c++11 -o map_lambda
在Ubuntu 15.10上编译。
我个人的意见是使用最后的解决方案,除非需要使用不同的功能进行密钥比较。
答案 2 :(得分:1)
这是答案,我担心你不会喜欢它。
如果没有逻辑错误,您无法做的事情。
(此时请阅读代码下方的文字。这很重要!)
如果可行,那就是这样......
#include <unordered_map>
#include <string>
#include <cstdint>
#include <utility>
#include <cassert>
struct A {};
struct B{};
int main()
{
auto owner_equal = [](const auto& l, const auto& r)
{
return not l.owner_before(r)
and not r.owner_before(l);
};
using equal_type = decltype(owner_equal);
std::unordered_map<
std::weak_ptr<A>,
B,
std::hash<std::weak_ptr<A>>,
equal_type> my_map;
auto pa = std::make_shared<A>;
my_map.emplace(pa, B{});
auto wa = std::weak_ptr<A>(pa);
pa.reset();
assert(my_map.find(wa) != my_map.end());
}
那么问题是什么?
你知道我如何比较weak_ptr的平等吗?这是了解他们是否引用同一个对象的唯一安全方法。您必须比较控制块的地址。这是因为,一旦插入地图,最后一个shared_ptr就会消失。
下次您将weak_ptr锁定在地图键中以获取对象地址时,您将获得nullptr。因此密钥不稳定,这是unordered_map的要求。
和?
计算哈希有同样的问题,但更糟。因为您无法访问控制块的地址,只能访问与其他地址相关的相对顺序。
因此,您无法可靠地计算weak_ptr的哈希值,因为当您调用lock()时,两个后续调用的答案可能会有所不同。
真的没有解决方案吗?
绝对不是。 lock()
解决方案似乎可以用于测试,但是当您的代码出现在生产环境中时,它会随机且无法解释地失败。
然后是什么?
您必须使用std::map
并接受O(logN)查找性能。