简体   繁体   English

在模板上下文中指向成员 function 或 static function 的指针

[英]Pointer to member function or to static function in template context

I am using pointer to member function in generic context and It works OK.我在通用上下文中使用指向成员 function 的指针,它工作正常。

struct Mock{
    static int inc(int){
        return 0;
    }

    static int dec(int){
        return 0;
    }
};

struct Real{
    Real(int v) : v(v){}

    int inc(int a) const{
        return a + v;
    }

    int dec(int a) const{
        return a - v;
    }

private:
    int v;
};

template<typename C, typename F>
auto user(C &c, F func){
    return (c.*func)(5);
}

int main(){
    Real real(5);

    return user(real, &Real::inc);
}

However if I try to pass static method (in case of Mock ), it stops working, because static method is like normal function.但是,如果我尝试通过 static 方法(在Mock的情况下),它会停止工作,因为 static 方法就像普通的 function 一样。

What I need to change in user function, so this compiles and work properly?我需要在user function 中进行更改,以便编译并正常工作?

I was able to do it with lambda, but it was way more boilerplate code.我可以用 lambda 做到这一点,但它是更多样板代码。

I am thinking of SFINAE or constexpr if, but I am not sure how to detect if method is static.我正在考虑 SFINAE 或 constexpr if,但我不确定如何检测方法是否为 static。

int main(){
    Mock real;

    return user(real, &Mock::inc);
}

I am using C++17.我正在使用 C++17。

std::is_member_pointer can be used to detect pointers to members. std::is_member_pointer可用于检测指向成员的指针。 You can then do a simple if constexpr to vary the behavior between those and a callable that should accept just the argument.然后,您可以做一个简单的if constexpr来改变这些和应该只接受参数的可调用对象之间的行为。

template<typename C, typename F>
auto user(C &c, F func){
    if constexpr (std::is_member_pointer_v<F>)
        return (c.*func)(5);
    else
        return func(5);
}

Alternatively, if you restructure your Mock a bit或者,如果你稍微重组你的Mock

struct Mock{
    static int inc(Mock const&, int){
        return 0;
    }

    static int dec(Mock const&, int){
        return 0;
    }
};

Then you can simply use std::invoke然后你可以简单地使用std::invoke

template<typename C, typename F>
auto user(C &c, F func){
    return std::invoke(func, c, 5);
}

With overload and SFINAE:过载和 SFINAE:

template<typename C, typename F>
auto user(C& c, F func) -> decltype((c.*func)(5)) {
    return (c.*func)(5);
}

template<typename C, typename F>
auto user(C&, F func) -> decltype(func(5)) {
    return func(5);
}

With if constexpr , you might do:使用if constexpr ,您可以这样做:

template<typename C, typename F>
auto user([[maybe_unused]]C& c, F func) {
    if constexpr (std::is_invocable_v<F, C, int>) {
        return std::invoke(func, c, 5);
    } else {
        static_assert(std::is_invocable_v<F, int>);
        return std::invoke(func, 5);
    }
}

My 5 cents.我的 5 美分。

This is what I come up with, after read @StoryTeller answer:这就是我在阅读@StoryTeller 的回答后得出的结论:

#include <type_traits>
#include <functional>

namespace class_invoke_impl_{
    template <class T, class F, class... Args>
    constexpr auto class_invoke_(T &&cl, F func, std::true_type, Args&&... args){
        return (std::forward<T>(cl).*func)(std::forward<Args>(args)...);
    }

    template <class T, class F, class... Args>
    constexpr auto class_invoke_(T const &, F func, std::false_type, Args&&... args){
        return func(std::forward<Args>(args)...);
    }
}

template <class T, class F, class... Args>
constexpr auto class_invoke(T &&cl, F func, Args&&... args){
    using namespace class_invoke_impl_;

    return class_invoke_(std::forward<T>(cl), func, std::is_member_pointer<F>{}, std::forward<Args>(args)...);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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