可变参数函数模板的指针

5

我有一个简单的类 A,提供一个可变参数函数模板。此函数使用来自 A 内部的私有数据,但函数本身是公开的。该类如下:

class A {
public:

    A() :
    _bla("bla: ") {
    }

    template <class T>
    void bar(const T& value) {
        std::cout << _bla << value << std::endl;
    }

    template <class H, class... T>
    void bar(const H& value, const T&... data) {
        std::cout << _bla << value << std::endl;
        bar(data...);
    }

private:
    const std::string _bla;
};

在一个名为foo.hpp的单独文件中,我有一个函数foo(),它应该能够接收并使用函数a.bar()作为参数:
int main(int argc, char *argv[]) {    
    A a;
    a.bar(1, "two", 3, 4);
    foo(&a.bar);
}

我不太确定从哪里开始,但我尝试了以下操作-但没有成功。请问如何正确完成此操作:
template <typename... T>
inline void foo(void (bar *)(const T&...)) {
    unsigned int x(0), y(0), z(0);
    bar(x, y, z);
}

奖励问题:是否有一种方法可以不仅调用:

foo(&a.bar);

还可以使用以下形式调用foo函数,将a.bar绑定到某些参数上:

foo(&(a.bar(p1, p2));

我可以简单地将 p1p2 添加到 foo 定义中,如下所示:

foo(p1, p2, &a.bar);

但如果我能在之前添加这些参数,从语义上讲,对我的目的更好一些。


1
为什么要用 enable_if 这种麻烦的东西呢?你可以直接重载它,写成 void bar() {} - jrok
1
顺便提一下,无需使用enable_if来判断参数包是否为空。只需有一个模板重载为(H,T...),以及一个没有参数的重载,什么都不做即可。 - Mike Seymour
foo 接受一个函数,但 a.bar 不是一个函数。它是一个模板。模板没有地址。有 a.bar<int>a.bar<double, char> 等函数;你想传递哪一个给 foo?此外,你如何区分 a.bar(p1, p2) 作为绑定到某些参数的函数和 a.bar(p1, p2) 作为应用于所有参数的完全应用函数? - n. m.
1个回答

9

如果不实例化函数模板,则无法传递其地址,因为这被视为整个重载集(无论模板是否为可变参数模板)。但是,您可以将其包装在通用函数对象中:

struct bar_caller
{
    template<typename... Ts>
    void operator () (A& a, Ts&&... args)
    {
        a.bar(std::forward<Ts>(args)...);
    }
};

然后让您的函数foo()定义如下:

template<typename F>
inline void foo(A& a, F f) {
    unsigned int x(0), y(0), z(0);
    f(a, x, y, z);
}

那么你在main()函数中的调用将变成:

int main()
{
    A a;
    a.bar(1, "two", 3, 4);
    foo(a, bar_caller());
}

很不幸,目前在C++中没有一种简单的方法可以将重载集合包装在一个函数对象中,而不需要定义一个单独的类 - 就像上面为bar_caller所做的那样。

编辑:

如果您不想直接向foo()传递一个A对象,则仍然可以让您的bar_caller封装对应该调用函数bar()A对象的引用(只需注意对象生命周期,以避免使该引用悬挂):

struct bar_caller
{
    bar_caller(A& a_) : a(a_) { }

    template<typename... Ts>
    void operator () (Ts&&... args)
    {
        a.bar(std::forward<Ts>(args)...);
    }

    A& a;
};

您可以按照以下方式重新编写foo()main()函数:
template<typename F>
inline void foo(F f) {
    unsigned int x(0), y(0), z(0);
    f(x, y, z);
}

int main()
{
    A a;
    a.bar(1, "two", 3, 4);
    foo(bar_caller(a));
}

+1 Andy,非常感谢您的回答!在实际情况中,我有一个继承其他类并包含A的小类的类。如果我要在A#include我的外部类——实际上是包含在外部类中的——我想我已经有的混乱将变成史诗般的意大利面条^^我并不介意额外的参数,我可以将它们添加到foo定义中,但我真的不想把A——我的外部类——传递给foo。有没有可能的解决方法? - Rubens
Andy,我实际上将A作为外部函数,而bar是从继承中可用的函数。直到现在,我一直将变量作为引用传递给foo,以便foo填充变量,并且我会在A本身中调用bar,使用我从foo中检索的值。我只是添加了一些上下文来使它更清晰。我正在检查您的编辑。 - Rubens
完美的解决方案!我想现在我能够做到我想要的东西了!只有一个最后的问题,void operator()(Ts&&... args){}中的双重引用具体是什么意思? - Rubens

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接