简体   繁体   中英

Restrict function template for member functions only

I am working in C++11 and have the following code that compiles. But the problem is that the function func in the below example can also be called with a std::function , lambda, pointer to a function etc.

Instead, I want that func should only be called by a pointer to a non-static member function of any class. That is, I want to restrict this function only member function pointers.

template <typename Callable, typename... Args> void func(Callable&& callable, Args&&... args)
{

}
struct Test
{
    int someMember(int x) 
    { 
        return x; 
    }
};
void g(int, int, int) 
{

}
int main()
{
    func(g, 1, 1, 1);     //this works currently but it should be rejected in the modified program
    func([](int){}, 42);  //this works currently but it should be rejected in the modified program 
    
    Test test;
    func(&Test::someMember, test, 1);// this works currently and should work in the modified version
}

As we can see in the above program, all of the calls to func works. But I want that only the call func(&Test::someMember, test, 1); should work and the other two calls should be rejected.

So how can I achieve this. Maybe there is a way to use SFINAE or some other metaprogramming technique.

Maybe there is a way to use SFINAE or some other metaprogramming technique.

That would do it, since we have std::is_member_function_pointer .

template <typename Callable, typename... Args>
typename std::enable_if<std::is_member_function_pointer<Callable>::value, void>::type
func(Callable callable, Args&&... args)
{

}

If the predicate is false, enable_if produces no type, and our template has no return type, making the function non-viable.

The change to pass by value is because it makes to controlling condition simpler, and because we are only passing pointers to members (fairly cheap to copy).

I think static_assert (with std::is_member_function_pointer ) is the perfect tool for the situation. No need to change the signature of func , and the error message can be whatever you want, so it's clearer than, for example, a substitution failure.

To call callable , you may use std::mem_fn .

template <typename Callable, typename... Args>
void func(Callable callable, Args&&... args)
{
    static_assert(std::is_member_function_pointer<Callable>::value, "callable must be a member function");

    std::mem_fn(callable)(args...);
}

Demo

This can be done by setting up the template parameters in such a way that only pointers to member function are accepted(as shown below). In particular, we can have 4 template parameters corresponding to the class, member function parameters, object of that class and finally the arguments passed to that member function.

template<typename className, typename... Param,typename Ret, typename... Args>  
void func(Ret (className::*ptrFunc)(Param... param),className& Object, Args... args)
{
    (Object.*ptrFunc)(args...); //std::invoke(ptrFunc, Object, args...) in C++17
}
int main()
{
    Test test;
    func(&Test::someMember, test, 1);//only this form works now  
}

Working demo

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM