2015-02-03 5 views
2
이 멤버 함수가 존재하는지 여부를 확인에 대한 대답 질문의 수입니다

: 예를 들어, Is it possible to write a template to check for a function's existence?(과부하) 멤버 함수의 존재를 확인

는하지만 기능이 과부하되는 경우이 방법이 실패합니다. 그 질문의 최상위 답변에서 약간 수정 된 코드입니다.

#include <iostream> 
#include <vector> 

struct Hello 
{ 
    int helloworld(int x) { return 0; } 
    int helloworld(std::vector<int> x) { return 0; } 
}; 

struct Generic {}; 


// SFINAE test 
template <typename T> 
class has_helloworld 
{ 
    typedef char one; 
    typedef long two; 

    template <typename C> static one test(decltype(&C::helloworld)) ; 
    template <typename C> static two test(...); 


public: 
    enum { value = sizeof(test<T>(0)) == sizeof(char) }; 
}; 


int 
main(int argc, char *argv[]) 
{ 
    std::cout << has_helloworld<Hello>::value << std::endl; 
    std::cout << has_helloworld<Generic>::value << std::endl; 
    return 0; 
} 

이 코드를 출력합니다 :

0 
0 

그러나 :

1 
0 

두 번째 helloworld()이 주석합니다.

내 질문은 그것이 오버로드 여부에 관계없이 멤버 함수가 있는지 여부를 확인할 수 있는지 여부입니다.

+0

당신이 알고 싶은 수행합니다 SFINAE-수 맥락에서, 예를 들면 : 적절하게 호출 할 수있는 등의 구성원이있는 경우

template <typename T, typename... Args> class has_helloworld { template <typename C, typename = decltype(std::declval<C>().helloworld(std::declval<Args>()...))> static std::true_type test(int); template <typename C> static std::false_type test(...); public: static constexpr bool value = decltype(test<T>(0))::value; }; 

그런 다음 확인하려면이 형식을 사용하십시오 특정 매개 변수로 호출 된 경우 함수가 존재합니다 *? 어떤 컴파일러를 지원해야합니까? 어떤 표준 버전입니까? – Yakk

+0

모든 매개 변수가 있는지 확인하는 것으로 충분합니다. 저는 C++ 11에 만족 합니다만 가능한 한 많은 컴파일러를 지원하고 싶습니다. – foxcub

+0

컴파일러에 관해서, 필자는 주어진 코드가 Clang 3.5.1에서'0 0'을 컴파일하고 출력하지만 GCC 4.9.2에서는 컴파일되지 않는다는 것을 알아 챘습니다. – foxcub

답변

9

C++에서는 오버로드 집합의 주소를 가져올 수 없습니다. 함수 또는 멤버 함수의 주소를 가져올 때 함수가 고유하거나 적절한 포인터를 선택해야 할 필요가 있습니다. 예를 들어 포인터를 적절한 함수에 즉시 전달하거나 캐스팅하여. 다르게 표현하면 helloworld이 고유하지 않은 경우 표현 &C::helloworld이 실패합니다. 늘어나는 이름이 클래스 멤버 또는 표준 함수로 존재하는지 여부를 확인할 수 없다는 결과가 나왔습니다.

일반적으로 이름을 사용하여 작업해야합니다. 즉, 함수가 존재하는지 알기에 충분하다면 은 지정된 유형의 인수 세트로 호출 할 수 있습니다.이 질문은 많은 차이가 있습니다.이 질문은 해당 호출을 시도하고 해당 유형을 판별하여 응답 할 수 있습니다

std::cout << std::boolalpha 
      << has_helloworld<Hello>::value << '\n'  // false 
      << has_helloworld<Hello, int>::value << '\n' // true 
      << has_helloworld<Generic>::value << '\n'; // false 
+0

Super. 설명 해줘서 고마워. – foxcub

2
// use std::void_t in C++... 17 I think? ... instead of this: 
template<class...>struct void_type { using type = void; }; 
template<class...Ts>using void_t = typename void_type<Ts...>::type; 

template<class T, class...Args> 
using hello_world_ify = decltype(
     std::declval<T>().helloworld(std::declval<Args>()...) 
); 

template<class T, class Sig, class=void> 
struct has_helloworld:std::false_type{}; 

template<class T, class...Args> 
struct has_helloworld<T, void(Args...), 
    void_t< 
    hello_world_ify<T, Args...> 
    > 
>:std::true_type{}; 

template<class T, class R, class...Args> 
struct has_helloworld<T, R(Args...), 
    typename std::enable_if< 
    std::is_convertible< 
     hello_world_ify<T, Args...>, 
     R 
    >::value 
    >::type 
>:std::true_type{}; 

live example

은 내가 details 네임 스페이스에 위를 넣어, 다음 template<class T, class Sig> struct has_helloworld:details::has_helloworld<T,Sig>{}; 그래서 누군가가 부도 void 대신에 종류를 통과하지 못한 노출 것입니다.

T.helloworld(Args...)을 호출 할 수 있는지 여부를 감지하기 위해 SFINAE를 사용합니다. 전달 된 서명이 void(blah) 인 경우 호출이 발생할 수 있는지 여부 만 감지합니다. 그렇지 않은 경우 반환 유형 T.helloworld(Args...)을 서명의 반환 유형으로 변환 할 수 있는지 테스트합니다.

MSVC는 decltype으로 SFINAE를 수행하는 데 중대한 문제가 있으므로 위의 내용이 MSVC에서 작동하지 않을 수 있습니다. has_helloworld<T, R(Args...)>Args... r- 수치가 통과하는 r- 수치 컨텍스트 helloworld 호출, r- 수치 T 전달에 해당

참고. 값을 lvalues로 만들려면 &을 추가하십시오. const lvalues로 만들려면 유형에 const&을 사용하십시오. 그러나 이것은 대부분 일부 경우에만 중요합니다.

보다 일반적인 경우 일치시킬 샘플 서명을 지정하지 않아도 대체 된 메소드의 존재를 감지 할 수있는 방법이 없습니다.

위의 내용은 정확한 서명 일치를 처리하기 위해 조정될 수 있습니다.

재미있게도 서명 충돌이있는 경우 (즉, 호출의 직접적인 컨텍스트에서 오류가 발생하는 경우) 해당 메서드가없는 것처럼 동작합니다.

SFINAE에 의존하기 때문에 비 즉각적인 컨텍스트의 오류로 인해 오류가 트리거되지 않습니다.