将函数和成员函数都传递给模板方法

时间:2018-08-19 17:30:04

标签: c++ templates

我已经研究了一段时间,希望在这里得到澄清。

我已经开发了Template类。其方法之一是输入具有不同参数的两个函数,如下所示:

template<typename S, typename T, typename R>
void TGraph<S,T,R>::BFS(const S &key, bool(*relValid)(R), void(*manip)(string, string, R)) {
    //Being here R,S members of the template class TGraph, do stuff including
    if(relValid("some R") ){
    manip("some S", "another S", "someR");
    }

}

当在main.cpp中传递标准函数时,这就像一种魅力。

int main(){
    TGraph2<string, User, Relation> graph;
    graph.BFS("Rube", alwaysValid, printRelation);
}
void printRelation(string id1, string id2, Relation rel){
    cout<<id1<<" ha una relazione con "<<id2<<" di tipo "<<rel.type<<endl;
}
bool alwaysValid(Relation rel){
    return true;
}

在另一个类中使用模板类并改为使用成员函数时,我无法编译。

class DataSet {
private:
    TGraph<string, User, Relation> _users;
....
}

我发现这是由于指针到函数类型和指针到成员函数类型之间的差异。 一种解决方案是使成员成为静态成员,这对我而言是不可能的(我需要访问一些非静态成员).``

现在我想知道,是否存在一些“干净”的解决方案来允许BFS方法与任何函数一起工作(无论是否为成员)? 或者至少使其与成员函数一起使用(实际上是我的需要)。

1 个答案:

答案 0 :(得分:0)

成员函数和非成员函数完全不同。结果,它们混合得不好。解决此问题的方法是接受函数对象而不是函数指针或指向成员函数的指针是接受函数对象。这些仍然不一定会立即接受成员函数,但是仔细实现结果函数可以透明地处理所有函数对象。

要处理功能对象,大致有两种方法:

  1. 接受功能对象作为模板参数。这样做对性能非常有用:如果编译器可以通过调用链看到它们,则可以愉快地内联所有操作(通过传递lambda函数而不是函数指针可以使它们相当成功)。这种方法的缺点是需要公开功能模板。取决于上下文,这可能会带来问题。另外,所需的签名在函数签名中不可见,需要在其他位置指定。

    您可以如下编写您的BFS()成员。 std::invoke()的使用处理了传递任何可以被认为是函数对象的东西,包括成员函数指针和成员指针:

    template<typename S, typename T, typename R, typename RelValid, typename Manip>
    void TGraph<S,T,R>::BFS(const S &key, RelValid relValid, Manip manip) {
        //Being here R,S members of the template class TGraph, do stuff including
        if(std::invoke(relValid, "some R") ){
            std::invoke(manip, "some S", "another S", "someR");
        }
    }
    
  2. 接受仅指定函数接口的类型擦除的函数对象。对于类型擦除的函数对象,std::function<RC(T...)>是显而易见的首选。通常,函数指针,成员函数指针和函数对象可以轻松转换为兼容的std::function<RC(T...)>类型。

    使用std::function<...>替换函数指针很简单:函数指针已经使用了签名,该签名是std::function<...>的模板参数所需要的:

    template<typename S, typename T, typename R>
    void TGraph<S,T,R>::BFS(const S &key, std::function<bool(R)> relValid, std::function<void(string, string, R)> manip) {
        //Being here R,S members of the template class TGraph, do stuff including
        if(relValid("some R") ){
            manip("some S", "another S", "someR");
        }
    }
    

哪种选择方法在某种程度上取决于预期的用途。如果函数对象被大量使用,例如,它为访问的每个节点调用一次,则我更喜欢使用模板参数。该功能仅使用了几次,或者需要在非模板化界面中显示,我将使用std::function<...>(或类似名称)。