불변성에 관계없이 포인터-메소드를 취하는 함수

Aug 19 2020

개체에 대한 참조와 해당 멤버 함수에 대한 포인터를 가져와 호출하는 일반 함수를 구현하고 싶습니다. 그러나 두 개의 오버로드를 제공해야하므로 내 클래스에 const 및 non-const 메서드가 모두있을 때 그렇게 할 수 없습니다.

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...))
{
    return (object.*method)(Us{}...);
}

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...) const)
{
    return (object.*method)(Us{}...);
}

const 및 non-const 메서드 포인터를 모두 허용하는 템플릿 함수를 하나만 작성하는 방법이 있으므로 코드를 두 번 작성할 필요가 없습니까? C ++ 14를 사용하고 있습니다.

더 넓은 그림을 위해 궁극적으로 달성하고자하는 것은 메서드 인수가 추출되는 데이터 버퍼 인 세 번째 매개 변수를 전달하는 것입니다. 따라서 가능한 한 일반적으로이를 처리하는 템플릿 함수입니다.

답변

2 TedLyngmo Aug 19 2020 at 19:58

다음은 std::function.

내가 궁극적으로 달성하고 싶은 것은 메서드 인수가 추출되는 데이터 버퍼 인 세 번째 매개 변수를 전달하는 것입니다. 따라서 가능한 한 일반적으로이를 처리하는 템플릿 함수

통화 사이트에서 사용하는 내용은 여기에 완벽하게 전달됩니다.

template<typename Class, typename Func, typename... Args>
decltype(auto) callMethod_impl(Class& object, Func method, Args&&... args) {
    return (object.*method)(std::forward<Args>(args)...);
}


template<typename Ret, typename Class, typename... Us, typename... Args>
Ret callMethod(Class& object, Ret(Class::*method)(Us...), Args&&... args) {
    return callMethod_impl(object, method, std::forward<Args>(args)...);
}

template<typename Ret, typename Class, typename... Us, typename... Args>
Ret callMethod(const Class& object, Ret(Class::*method)(Us...) const, Args&&... args) {
    return callMethod_impl(object, method, std::forward<Args>(args)...);
}

데모

Ret에서 필요한 경우 callMethod_impl템플릿 매개 변수로 추가 callMethod_impl<Ret>(...)하고 callMethod오버로드 ( Demo ) 에서처럼 호출합니다 .

3 JanSchultke Aug 19 2020 at 17:28

짧은 대답은 직접 구현하지 마십시오. 이미 다음과 같은 형식으로 수행되었습니다 std::invoke.

#include <functional>

struct A {
    void foo(int x);
    void bar(int x) const;
};

void example() {
    A a;
    std::invoke(&A::foo, a, 3); 
    std::invoke(&A::bar, a, 3); 
}

돌이켜 보면 C ++ 14 태그를 추가했음을 확인한 후의 문서에 std::invoke프로젝트에서 사용할 수있는 샘플 구현이 있습니다.

1 Jodocus Aug 19 2020 at 17:59

를 사용하지 않으면 std::invoke유형 삭제에 따라 다소 일반적인 해결 방법 만 수행 할 수 있습니다. 를 사용 std::function하면 동일한 기반에서 함수를 처리하는 프록시 함수를 만들 수 있습니다.

#include <iostream>
#include <functional>

template<typename Ret, typename ...Us>
Ret callMethod_impl(std::function<Ret(Us...)> f) {
    // common implementation here
    return f(Us{}...);
}

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...)) {
    return callMethod_impl(std::function<Ret(Us...)>(std::bind(method, object)));
}

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(const Class &object, Ret (Class::*method)(Us...) const) {
    return callMethod_impl(std::function<Ret(Us...)>(std::bind(method, object)));
}

struct Foo {
    int bar() const { return 1; }
    float bar() { return 2.1f; };
};

int main() {
    Foo f;
    std::cout << callMethod(f, &Foo::bar) << std::endl;
}

참고 것으로 volatile기능 (+ 조합) 여기에 처리되지 않은,하지만 어쩌면 당신은 전체 일반성이 아니라 실용적인 솔루션을 필요로하지 않습니다.