变量模板作为std :: function的参数

时间:2017-09-29 13:58:33

标签: c++ function c++11 variadic-templates

我想构建一个允许我用未定义数量的参数调用成员函数的结构。现在我写了这样的东西

template<typename Class, typename Return, typename ... Args>
struct Caller
{
private:
    std::function<Return(Args ...)> callerFunction;

    Caller() = delete;
    Caller(const Caller&) = delete;
    Caller(Caller&&) = delete;
    Caller& operator=(const Caller&) = delete;

public:
    ~Caller() = default;

    Caller(Class& instance, Return(Class::*function)(Args ...))
    {
        callerFunction = [&instance, function](Args... args)
        {
            return (instance.*function)(args ...);
        };
    }

    Return operator() (Args ... args)
    {
        return callerFunction(args ...);
    }
};

我担心我不能解决这个事实,即我不能将std :: function变量声明为std::function<Return<Args&& ...)> callerFunction

当我尝试这样做时,编译器说它无法从int转换为int&&(例如参数是int s),所以我猜测函数将Args&& ...视为右值引用的参数包。我是对的吗?

有解决方法吗?

编辑:好的,我以错误的方式在Caller构造函数中声明了这个函数。

错误的方式 - &gt; Caller(Class& instance, Return(Class::*function)(Args&& ...))

正确的方式 - &gt; Caller(Class& instance, Return(Class::*function)(Args ...))

(我猜)正确的实现是

template<typename Class, typename Return, typename ... Args>
struct Caller
{
private:
    std::function<Return(Args&& ...)> callerFunction;

    Caller() = delete;
    Caller(const Caller&) = delete;
    Caller(Caller&&) = delete;
    Caller& operator=(const Caller&) = delete;

public:
    ~Caller() = default;

    Caller(Class& instance, Return(Class::*function)(Args ...))
    {
        callerFunction = [&instance, function] (Args&&... args)
        {
            return (instance.*function)(std::forward<Args>(args) ...);
        };
    }

    Return operator() (Args&& ... args)
    {
        return callerFunction(std::forward<Args>(args) ...);
    }
};

现在的问题是:为什么我需要在没有double&amp;的情况下声明该函数?

2 个答案:

答案 0 :(得分:1)

当您像这样定义类模板时:

template <typename T>
struct A {
 A(T&& param)
}

然后创建一个实例:

A<int> myInstance(someIntVariable);

它不会编译。原因是,T的类型是由您明确指定的(int中的A<int>),而您的类构造函数参数不再是T&&,而是{ {1}},因此它不再是通用引用(它接受左值和右值引用),而是常规的右值引用。

接下来,如果你传递一些整数,则会出现类型不匹配错误,因为在期望rvalue引用时传递常规变量。

在你的例子中你明确定义了函数签名,所以同样适用 - 构造函数需要一个函数对rgs引用rval引用...但是这不是真的。

我认为更好地解释了in this question

答案 1 :(得分:0)

int&&int不同。如果您的方法采用int,则不会使用int&&。它们是不同的类型。

成员函数指针的不同签名之间没有转换。

转发引用并不神奇。它们依赖于演绎规则(并且在推导的上下文中不使用上面的Args&&...)和参考折叠规则。

因此,你的

Return operator() (Args&& ... args)

也错了。如果Args...int,则您无法通过int a; blah.operator()(a)调用上述内容,因为a无法绑定int&&

老实说,您的整个类型应该被抛弃并替换为std::function<Return(Args...)>Class对函数的作用是无用的缩小,而且你的包装也不会增加太多。

如果用户确实需要复制构造函数,或者使用[&a](auto&&...args)->decltype(auto){ return a.foo(decltype(args)(args)...); }

,则用户可以std::bind( &A::foo, std::ref(a) )