变量函数模板不能很好地使用std::function作为参数。

问题描述 投票:0回答:1

我正在尝试创建一个安全的 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>>
c++ lambda variadic-functions
1个回答
0
投票

让我们把你的例子缩减为以下内容。

#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); 
 }

0
投票

在花了几个小时摆弄这个问题后,我想不通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上改成布尔返回, 我只是想在我的实现中实现它。这种设置对我来说就很好。

© www.soinside.com 2019 - 2024. All rights reserved.