我正在尝试创建一个安全的 std::map
封装器.为了避免数据丢失的情况,避免误用导致线程重新同步的风险,我试图在该封装器中实现一个函数,可以直接操作内部的 std::map
实例而不破坏 std::lock_guard
的作用域。几个小时前,我让这个函数如愿以偿地工作了,但我决定把函数的定义改为使用 std::function
从 <functional>
而不是,因为有些操作太短,最好用lambda来运行。
我希望你们能告诉我我做错了什么。我相信这与函数的变量模板有关,因为消除了这一点,定义了函数却没有产生一个功能的例子。
旧的,工作格式。
template <class T, class U, class V = std::less<T>> class Map {
std::map<T,U,V> MAP;
mutable std::mutex LOCK;
public:
template <class... Args>
void performOperation(void(*funct)(std::map<T,U,V>&, Args&...), Args&... args){
std::lock_guard<std::mutex> lk (LOCK);
funct(MAP, args...);
}
};
Map<int, std::string> TSMap;
void functionThatDoesStuff(std::map<int, std::string>& tsm, const int& k, const std::string& v){
//doStuff
}
int memberFunctionOfAnotherClass(const int& key, const std::string& val){
TSMap.performOperation(functionThatDoesStuff, key, val);
}
工作的,非变量的
template <class T, class U, class V = std::less<T>> class Map {
std::map<T,U,V> MAP;
mutable std::mutex LOCK;
public:
void performOperation(std::function<void (std::map<T,U,V>&)> funct){
std::lock_guard<std::mutex> lk (LOCK);
funct(MAP);
}
};
Map<int, std::string> TSMap;
int memberFunctionOfAnotherClass(const int& key, const std::string& val){
TSMap.performOperation([](std::map<int, std::string>& tsm){
//doStuff
});
}
新的,破坏的格式。
template <class T, class U, class V = std::less<T>> class Map {
std::map<T,U,V> MAP;
mutable std::mutex LOCK;
public:
template <class... Args>
void performOperation(std::function<void (std::map<T,U,V>&, Args...)> funct, Args&... args){
std::lock_guard<std::mutex> lk (LOCK);
funct(MAP, args...);
}
};
Map<int, std::string> TSMap;
int memberFunctionOfAnotherClass(const int& key, const std::string& val){
// I have tried every different combination of const and ampersand-based referencing here to no avail
// v v
TSMap.performOperation([](std::map<int, std::string>& tsm, int k, std::string v){
//doStuff
}, key, val);
}
第三个代码块产生的错误是:
no instance of function template "Map<T,U,V>::performOperation [with T=int, U=std::string, V=std::less<int>]" matches the argument list
argument types are: (lambda []void (std::map<int, std::string, std::less<int>, std::allocator<std::pair<const int, std::string>>> &tsm, int k, std::string v)->void, const int, const std::string)
object type is: Map<int, std::string, std::less<int>>
让我们把你的例子缩减为以下内容。
#include <functional>
template <class T>
void bad_foo(std::function<void(T)>, T)
{}
int main() {
bad_foo([](int){}, 1);
}
当编译器专用于... bad_foo
函数模板,它试图根据你传递给函数的参数,推导出模板参数的类型。
这两个参数都在 "推导上下文 "中,所以编译器会尝试推导这两个参数。虽然它能够推导出 T
从第二个论点出发,第一个论点的推论失败--因为lambda不是 std::function
. 请注意,编译器在此阶段不进行任何转换!在此阶段,编译器不进行任何转换。
绕过这个问题的最简单方法是将第一个参数放在 不可推断的语境.
像这样:
#include <functional>
template <class T>
struct undeduce {
using type = T;
};
template <class T>
using undeduce_t = typename undeduce<T>::type;
template <class T>
void good_foo(undeduce_t<std::function<void(T)>>, T)
{}
int main() {
good_foo([](int){}, 1);
}
顺便说一下,有一个很常见的现实生活中的例子,它展示了几乎相同的问题。想一想为什么下面的代码不会编译,以及如何解决这个问题:-)。
#include <numeric>
#include <vector>
int main() {
std::max(std::vector<int>{}.size(), 1);
}
在花了几个小时摆弄这个问题后,我想不通Igor R提出的抑制类型扣减的建议应该如何实现,我不满足于抑制错误的想法,我开始测试其他方案。
结果我在发布这个问题的时候,已经有了一个完美的答案。我还是想不通为什么模板会破坏函数,但结合第一个和第二个例子,已经构建并按预期工作了类似下面的东西。
template <class T, class U, class V = std::less<T>> class Map {
std::map<T,U,V> MAP;
mutable std::mutex LOCK;
public:
template <class... Args>
void performOperation(void(*funct)(std::map<T,U,V>&, Args&...), Args&... args){
std::lock_guard<std::mutex> lk (LOCK);
funct(MAP, args...);
}
bool performOperation(std::function<bool (std::map<T,U,V>&)> funct){
std::lock_guard<std::mutex> lk (LOCK);
return funct(MAP);
}
};
Map<int, std::string> TSMap;
void functionThatDoesStuff(std::map<int, std::string>& tsm, const int& k, const std::string& v){
//doStuff
}
int memberFunctionOfAnotherClass(const int& key, const std::string& val){
TSMap.performOperation(functionThatDoesStuff, key, val);
TSMap.performOperation([&](std::map<int, std::string>& tsm)->bool{
//doStuff, key and val are available
return true;
});
}
没有必要在lambda上改成布尔返回, 我只是想在我的实现中实现它。这种设置对我来说就很好。