一个可以接受未知签名的变量函数引用的类

时间:2016-12-02 22:57:17

标签: c++ lambda variadic-templates

我想创建一个类,而不是在实例化时,接受可变数量的函数引用,这些引用没有预先知道的签名。这是一个几乎可以实现我想要的例子:

// To show the function refs being used 
void p(int arg) { cout << "int " << arg << endl; }
void p(string arg) { cout << "string " << arg << endl; }
void p(int arg1, int arg2) { cout<<"int/int "<<arg1<<arg2<<endl; }
void p(int arg1, string arg2) { cout<<"int/string "<<arg1<<arg2<<endl; }

class foo {
    public:
    // CTOR takes variadic function refs 
    template <typename... Args>
    foo(Args... args) { p(args()...); }
    // "args()..." requires supplied functions to take no parameters 
    // but makes no requirement on their return values. 
};

// Using lambdas, but free functions, std::bind(), etc. work too
foo i([]{return 1;});              // prints "int 1" 
foo s([]{return string("one");});  // prints "string one" 
foo b([]{return 2;},
      []{return string("two");});  // prints "int/string 2two"

我无法看到如何解决这个问题,因此在构造函数中不会评估作为参数提供的函数。我希望稍后通过p(args()...)中的另一种方法调用foo。这就是为什么foo不能像foo i(1)一样简单地创建的原因:参数函数需要稍后调用,多次调用,而不是仅在创建对象时调用(并且它们会更复杂)而不只是返回一个常数)。

问题似乎归结为保存对稍后要调用的构造函数参数的引用,当类不知道这些参数将具有多少或哪些签名时。不知何故,参数需要成为类模板的一部分而不仅仅是构造函数模板,而是如何?

如果传递的函数都具有相同的签名,则可以使用具有非类型参数的类模板,并将函数作为模板参数提供:

template <int (&...Arg)()>
class bar {
    public:
    bar() { p(Arg()...); }
    other() { p(Arg()...); } // Use in any method
};

int one() { return 1; }
int two() { return 2; }
bar<one> bi;       // Prints "int 1"
bar<one, two> bii; // Prints "int/int 12"

但这要求所有参数都是返回int的函数,并且不能与lambdas一起使用,因为它们不能是模板参数。

1 个答案:

答案 0 :(得分:2)

你可以使用lambda和std::function来做到这一点 请注意,lambda可以捕获参数包,然后(让我说)稍后将其解压缩 它遵循一个最小的工作示例:

#include<iostream>
#include<functional>

void p(int arg) { std::cout << "int " << arg << std::endl; }
void p(std::string arg) { std::cout << "string " << arg << std::endl; }
void p(int arg1, int arg2) { std::cout<<"int/int "<<arg1<<arg2<<std::endl; }
void p(int arg1, std::string arg2) { std::cout<<"int/string "<<arg1<<arg2<<std::endl; }

class foo {
public:
    template <typename... Args>
    foo(Args... args): func{[args...](){ p(args()...); }} {}

    void operator()() { func(); }

private:
    std::function<void()> func;
};

int main() {
    // create your objects...
    foo i([]{return 1;});
    foo s([]{return std::string("one");});
    foo b([]{return 2;}, []{return std::string("two");});
    // ... and use them later
    i();
    s();
    b();
}