简体   繁体   中英

how to pass non-static member function to template argument?

For a class template with function template argument, I hope to make it a member of my class with one of my class's function as its template argument, or use a lambda to call a member function. Here is rough example of what I want to do but I cannot get it working.

#include <iostream>

template<std::string (*fun)()>
struct wrapper{
    void operator()() {std::cout<<"wrapped "<<fun()<<std::endl;}
};

std::string foo() {
    return "foo";
}

struct bar{
    // static function works:
    static std::string str1() { return "bar1";}
    wrapper<str1> wrapped_bar1;

    // Non-static member function does not work:
    // std::string str2() { return "bar2";}
    // wrapper<&bar::str2> wrapped_bar2;

    // Lambda with *this does not work either:
    // static std::string str3(std::string input) { return input + "bar3";}
    // wrapper<[this](){return this->str3("bar")}> wrapped_bar3;
};

int main() {
    wrapper<foo> wrapped;
    wrapped();

    bar some_bar;
    some_bar.wrapped_bar1();
}

The type of &bar::str2 is std::string (bar::*)() and not std::string (*)() . There are ways to achieve the desired effect as shown below. In the below shown example, i have made wrapper to be an ordinary class-type instead of a class template. Moreover, the overloaded opeartor() is templated and is itself overloaded with a nontemplated version.

struct wrapper{ //wrapper is not a class template anymore 
    
    void operator()(std::string (*fun)()) {std::cout<<"wrapped "<<fun()<<std::endl;}
    template<typename T> 
    void operator()(std::string (T::*fun)(), T obj) 
    {
        
        std::cout<<"wrapped "<<(obj.*fun)()<<std::endl;
        
    }
};

std::string foo() {
    return "foo";
}

struct bar{
    // static function works:
    static std::string str1() { return "bar1";}
    wrapper wrapped_bar1;

    
    std::string str2() { return "bar2";}
    
};

int main() {
    wrapper wrapped;
    wrapped(foo);

    bar some_bar;
    some_bar.wrapped_bar1(&bar::str1);
//------------------------------------vvvvvvvv---->pass the object also on which non-static member function should be called    
    some_bar.wrapped_bar1(&bar::str2, some_bar);
}

Working demo

The output of the above modified program is:

wrapped foo
wrapped bar1
wrapped bar2

There's no way to make something of the form wrapper<&bar::str2> wrapped_bar2; just work as-is.

Consider:

struct bar {
    std::string str2() { return "bar2";}
    wrapper<&bar::str2> wrapped_bar2;
};

bar b1;
bar b2;

Now b1.wrapped_bar2 needs to have stored the pointer &b1 , and b2.wrapped_bar2 needs to have stored the pointer &b2 . This ( this ) pointer is the instance needed to call an instance (non-static) method.

It clearly can't be part of the static type (like a free function pointer, or the member function pointer itself), because b1 and b2 have the same type but different addresses. That means you need at least to write a constructor similar to

bar::bar() : wrapped_bar2{this} {}

Now, if we're having to do this, the two wrapper instantiations can't be used interchangeably, and we might just as well write a dedicated wrapper type for member functions:

template<typename Class, std::string (Class::*fun)()>
struct mem_wrapper{
    Class *instance_;

    explicit mem_wrapper(Class *that) : instance_(that) {}
    void operator()() {std::cout<<"wrapped "<< (instance_->*fun)() <<std::endl;}
};

If you want a single top-level type that can deal with both free functions and member functions, you need some way to hide these implementation differences (type erasure). Honestly, std::function already does all this for you, and it's not obvious there's a lot of benefit to reinventing this.

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