在昂贵功能数量变化的情况下是否可以使用 std :: call_once ?
想象一家工厂根据模板参数ResultType计算昂贵的东西,例如三角形网格的边缘邻接或平均顶点法线:
std::unordered_map<std::string, std::unique_ptr<AbstractResult>> mResults;
std::mutex mMutex;
template<typename ResultType, typename ...Params>
const ResultType& getResult(Params&&... params)
{
std::lock_guard<std::mutex> lock(mMutex);
const std::string& hash = ResultType::computeHash(std::forward<Params>(params)...);
auto it = mResults.find(hash);
if (it == mResults.end())
{
// Expensive call
mResults[hash] = std::make_unique<ResultType>(std::forward<Params>(params)...);
it = mResults.find(hash);
}
return *static_cast<ResultType*>(it->second.get());
}
仅在未创建结果的情况下才需要锁定。为了避免不必要的只读锁定,可以使用双重检查锁定。在C ++ 11中有各种各样的示例如何正确执行此操作,这不是问题。
但是,我的理解是std :: call_once为此提供了一种优雅的解决方案,应避免使用手动双重检查锁定。我知道如何将std :: call_once与单个std :: once_flag变量一起使用。但是,在我的示例中如何使用std :: call_once? 对于每个不同的ResultType,我需要一个std :: once_flag。如何存储一次标志的任意数?我无法将它们存储在用结果的哈希值处理的第二个unordered_map中,因为after_flags既不可复制也不可移动。我在这里想念一些明显的东西吗?
在编译时,已知所有可能的ResultTypes(以及std :: once_flags的数量),数量约为30。但是,没有所有可能的ResultTypes的列表,只有从对getResult<ResultType>
的所有调用中才能知道。如果我可以使用模板魔术来解决此问题,我将不胜感激,感谢您的输入。如果有任何特定于MSVC的解决方案,我也很乐意接受。