Template functor with any parameters

I'm trying to create template functor, which will take as arguments object and member function with any number of parameters. I can't figure out how to write the code correctly with templates.

template<typename ItemT,
     class T,
     typename ...Args>
struct Builder
{
    ItemT operator()(T& object, ItemT (T::*method)(Args...), Args && ... args)
    {
        return (object.*method)(std::forward<Args>(args)...);
    }
};

struct Object
{
    int method(int, int, int) { return 4; }
};


int main()
{
    Object obj;    
    Builder<int, Object>()(obj, &Object::method); // Error here
}

If I make Object::method with no parameters - code compiles. But with parameters - no.

Severity Code Description Project File Line Suppression State Error C2664 'int Builder::operator ()(T &,ItemT (__thiscall Object::* )(void))': cannot convert argument 2 from 'int (__thiscall Object::* )(int,int,int)' to 'int (__thiscall Object::* )(void)' drafts c:draftsmain.cpp 139


Assuming you don't want to change the current definition of Builder , this is how you need to instantiate it:

Builder<int, Object, int, int, int>()(obj, &Object::method, 0, 0, 0);
//      ^       ^    ^^^^^^^^^^^^^                          ^^^^^^^
//      ItemT   |    |                                      |
//              T    Args...                                args...

The args... parameter expansion in the operator() call must match the TArgs... pack passed to Builder itself.

wandbox example


Here's an alternative less strict design:

template<typename T>
struct Builder
{
    template <typename TFnPtr, typename... Args>
    auto operator()(T& object, TFnPtr method, Args && ... args)
    {
        return (object.*method)(std::forward<Args>(args)...);
    }
};

The above Builder can be used like this:

int main()
{
    Object obj;    
    Builder<Object>()(obj, &Object::method, 0, 0, 0);
}

In this case the type of member function pointer is deduced through TFnPtr and not constrained to any particular set of parameters. The variadic parameters are not part of Builder anymore - they're part of Builder::operator() , so they can be deduced and forwarded to (object.*method) .

wandbox example


You can avoid templating on Builder altogether and solely rely on template argument deduction:

struct Builder {
    template <typename Obj, typename R, typename ... FArgs, typename ... Args>
    R operator()(Obj& obj, R (Obj::*fn)(FArgs...), Args&&... args) {
        return (obj.*fn)(std::forward<Args>(args)...);
    }
};

I chose to use two parameter packs to allow perfect forwarding: the value categories of the operator() call do not necessarily match the targeted method. This also allows for implicit conversion of arguments when applying the member function pointer. Note that this implementation will not match const methods of Obj .

You can of course relax it a bit using auto return type (C++14) or trailing return type (C++11). Since Vittorio's answer already presented you the C++14 way, I'll tackle the latter. The operator() then becomes:

template <typename Obj, typename FnPtr, typename ... Args>
auto operator()(Obj& obj, FnPtr fn, Args&&... args)
    -> decltype((obj.*fn)(std::forward<Args>(args)...)) {
    return (obj.*fn)(std::forward<Args>(args)...);
}

Then, usage will simply be:

Object obj;
Builder()(obj, &Object::method, 0, 0, 0);

live demo on Coliru.

链接地址: http://www.djcxy.com/p/59568.html

上一篇: 修改数组的最后一个元素

下一篇: 具有任何参数的模板仿函数