【发布时间】:2020-05-15 21:49:09
【问题描述】:
我正在尝试创建一个线程安全的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>>
【问题讨论】:
-
你的 lambda 是无捕获的;它适用于函数指针。
-
无法推导出模板参数,因为 lambda 不是 std:: 函数(此阶段不发生转换)。尝试抑制对第一个函数参数中所有内容的扣除(使用模板
struct suppress{using type=T;}; 之类的东西包装 std:: 函数) -
@chris 我的印象是,通过捕获传递 lambda 会尝试在适当的位置运行 lambda,而不是将其作为参数传递。
New Broken Format在传递函数指针时也拒绝工作,就像我对Old Working Format所做的那样。 -
@IgorR。也许通过添加第二个模板类来替换 std::function 对象并在
performOperation中进行类型检查以确保传递的对象是有效的函数指针/lambda?喜欢template <class F, class... Args> void performOperation(F funct, Args&... args){?它似乎并不安全...... -
不知道为什么它不安全。如果您可以通过任何可调用对象 - 这是要走的路。 OTOH,如果你需要它是 std:: 函数,你必须抑制类型推导
标签: c++ templates lambda template-argument-deduction