const ref를 사용하는 포인터 대 멤버 함수에는 템플릿 함수가 작동하지 않습니다.


14

최근에 코드 반복을 해결하기 위해 템플릿 함수를 작성했습니다. 다음과 같이 보입니다 :

template<class T, class R, class... Args>
R call_or_throw(const std::weak_ptr<T>& ptr, const std::string& error, R (T::*fun)(Args...), Args... args) {
    if (auto sp = ptr.lock()) 
    {
        return std::invoke(fun, *sp, args...);
    }
    else 
    {
        throw std::runtime_error(error.c_str());
    }
}

int main() {
    auto a = std::make_shared<A>();
    call_or_throw(std::weak_ptr<A>(a), "err", &A::foo, 1);
}

이 코드는 class A다음과 같이 완벽하게 작동합니다 .

class A {
public:
    void foo(int x) {

    }
};

그러나 다음과 같이 컴파일하지 못합니다.

class A {
public:
    void foo(const int& x) {

    }
};

왜 그렇게 되었습니까 (왜 타입을 추론하지 못하는가) 왜이 코드를 참조로 작동하게 할 수 있습니까? 라이브 예


어쩌면 Args&&...std::forward?
fas

@ user3365922가 시도했습니다. 해결책 같은 느낌, 작동하지 않습니다
bartop

하지 않을 올바른 방향으로 도움 당신은?
기즈모

답변:


3

문제는 다음 Args사이에 충돌 공제가 있다는 것입니다 .

  • R (T::*fun)(Args...)
  • Args... args

나는 더 일반적인 코드 (사이에 중복하도록 제안 R (T::*fun)(Args...)하고
CONST 버전 R (T::*fun)(Args...) const과 다른 대안) :

template<class T, class F, class... Args>
decltype(auto) call_or_throw(const std::weak_ptr<T>& ptr,
                             const std::string& error,
                             F f,
                             Args&&... args)
{
    if (auto sp = ptr.lock()) 
    {
        return std::invoke(f, *sp, std::forward<Args>(args)...);
    }
    else 
    {
        throw std::runtime_error(error.c_str());
    }
}

멤버 함수의 cv-qualification에 대한 좋은 지적, 이것이 지금까지 최고의 솔루션이라고 생각합니다
bartop

8

Args종류는 모두 추론 할 수 없다 const&(상기 fun비 기준 매개 변수 선언)에서 args선언. 간단한 수정은 두 개의 개별 템플릿 유형 매개 변수 팩을 사용하는 것입니다.

template<class T, class R, class... Args, class... DeclaredArgs>
R call_or_throw(
    const std::weak_ptr<T>& ptr,
    const std::string& error,
    R (T::*fun)(DeclaredArgs...),
    Args... args);

단점으로, 잘못된 사용법의 경우 약간 더 긴 오류 메시지를 상상할 수 있습니다.


1
아마 당신이 원할 것입니다Args&&... args
Jarod42

5

템플릿 매개 변수 Args의 유형은 const int&세 번째 함수 인수 &A::foo에서와 같이 추론되고 int네 번째 함수 매개 변수에서 와 같이 추론됩니다 1. 일치하지 않으며 공제 실패를 유발합니다.

네 번째 파라미터를 추론 에서 제외 할 수 있습니다 . 예 :

template<class T, class R, class... Args>
R call_or_throw(const std::weak_ptr<T>& ptr, 
                const std::string& error, 
                R (T::*fun)(Args...), 
                std::type_identity_t<Args>... args) {
//              ^^^^^^^^^^^^^^^^^^^^^^^^^^                

라이브

PS : std::type_identityC ++ 20부터 지원됩니다. 그러나 구현하기는 매우 쉽습니다.


1
어떻게 든 완벽한 전달로 작동합니까?
bartop

@ bartop 그렇게 생각합니다. 우리는 즉, 전달 참조 스타일에 부합하는 4 매개 변수를 만들 수 있습니다 Args&&..., 다음 넣어 std::type_identity같은 제 3 파라미터에 R (T::*fun)(std::type_identity_t<Args>...). LIVE and LIVE
songyuanyao

@songyuanyo 예, 그러나 가치 주장을 깰 것입니다.
bartop

이미 코드 데모 에서 앞으로 사용할 수 있습니다 . "추가"이동 만합니다.
Jarod42
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.