문제

특정 멤버 함수가 클래스에서 정의되어 있는지에 따라 동작을 변경하는 템플릿을 작성할 수 있습니까?

다음은 내가 쓰고 싶은 내용의 간단한 예입니다.

template<class T>
std::string optionalToString(T* obj)
{
    if (FUNCTION_EXISTS(T->toString))
        return obj->toString();
    else
        return "toString not defined";
}

그래서 만약 class T 가지다 toString() 정의 된 다음 사용합니다. 그렇지 않으면 그렇지 않습니다. 내가하는 방법을 모르는 마법의 부분은 "function_exists"부분입니다.

도움이 되었습니까?

해결책

예, sfinae를 사용하면 주어진 클래스가 특정 방법을 제공하는지 확인할 수 있습니다. 작업 코드는 다음과 같습니다.

#include <iostream>

struct Hello
{
    int helloworld() { return 0; }
};

struct Generic {};    

// SFINAE test
template <typename T>
class has_helloworld
{
    typedef char one;
    struct two { char x[2]; };

    template <typename C> static one test( typeof(&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;
}

방금 Linux와 GCC 4.1/4.3으로 테스트했습니다. 다른 컴파일러를 실행하는 다른 플랫폼에 휴대용인지 모르겠습니다.

다른 팁

이 질문은 오래되었지만 C ++ 11을 사용하면 SFINAE에 다시 의존하는 기능 존재 (또는 유형이 아닌 멤버의 존재)를 확인하는 새로운 방법을 얻었습니다.

template<class T>
auto serialize_imp(std::ostream& os, T const& obj, int)
    -> decltype(os << obj, void())
{
  os << obj;
}

template<class T>
auto serialize_imp(std::ostream& os, T const& obj, long)
    -> decltype(obj.stream(os), void())
{
  obj.stream(os);
}

template<class T>
auto serialize(std::ostream& os, T const& obj)
    -> decltype(serialize_imp(os, obj, 0), void())
{
  serialize_imp(os, obj, 0);
}

이제 몇 가지 설명에. 먼저 사용합니다 표현 sfinae 제외하기 위해 serialize(_imp) 내부의 첫 번째 표현식 인 경우 과부하 해상도의 기능 decltype 유효하지 않습니다 (일명, 함수가 존재하지 않음).

그만큼 void() 모든 기능의 반환 유형을 만드는 데 사용됩니다. void.

그만큼 0 인수는 선호하는 데 사용됩니다 os << obj 둘 다 사용 가능한 경우 과부하 (리터럴 0 유형입니다 int 그리고 첫 번째 오버로드는 더 나은 일치입니다).


이제 기능이 존재하는지 확인하기 위해 특성을 원할 것입니다. 운 좋게도, 그것을 쓰기 쉽습니다. 그래도 특성을 써야한다는 점에 유의하십시오. 당신 자신 모든 다른 기능 이름에 대해 원할 수도 있습니다.

#include <type_traits>

template<class>
struct sfinae_true : std::true_type{};

namespace detail{
  template<class T, class A0>
  static auto test_stream(int)
      -> sfinae_true<decltype(std::declval<T>().stream(std::declval<A0>()))>;
  template<class, class A0>
  static auto test_stream(long) -> std::false_type;
} // detail::

template<class T, class Arg>
struct has_stream : decltype(detail::test_stream<T, Arg>(0)){};

라이브 예.

그리고 설명에. 첫 번째, sfinae_true 도우미 유형이며 기본적으로 쓰기와 동일합니다. decltype(void(std::declval<T>().stream(a0)), std::true_type{}). 장점은 단순히 짧다는 것입니다.
다음으로 struct has_stream : decltype(...) 어느 쪽이든 상속 std::true_type 또는 std::false_type 결국, decltype 체크인 test_stream 실패 여부.
마지막, std::declval 당신이 그것을 구성 할 수있는 방법을 알 필요없이 당신이 통과하는 모든 유형의 "가치"를 제공합니다. 이것은 평가하지 않은 맥락에서만 가능합니다. decltype, sizeof 다른 사람.


주목하십시오 decltype 반드시 필요한 것은 아닙니다 sizeof (그리고 모든 평가하지 않은 상황)는 그 향상을 얻었습니다. 그저 그게 다 decltype 이미 유형을 제공하며 더 깨끗합니다. 여기에 있습니다 sizeof 과부하 중 하나의 버전 :

template<class T>
void serialize_imp(std::ostream& os, T const& obj, int,
    int(*)[sizeof((os << obj),0)] = 0)
{
  os << obj;
}

그만큼 int 그리고 long 같은 이유로 매개 변수가 여전히 있습니다. 배열 포인터는 sizeof 사용할 수 있습니다.

C ++ 허용 sfinae 이를 위해 사용하려면 (C ++ 11 기능의 경우 거의 임의의 표현식에서 확장 된 SFINAE를 지원하기 때문에 단순화됩니다. 아래는 공통 C ++ 03 컴파일러와 함께 작동하도록 제작되었습니다).

#define HAS_MEM_FUNC(func, name)                                        \
    template<typename T, typename Sign>                                 \
    struct name {                                                       \
        typedef char yes[1];                                            \
        typedef char no [2];                                            \
        template <typename U, U> struct type_check;                     \
        template <typename _1> static yes &chk(type_check<Sign, &_1::func > *); \
        template <typename   > static no  &chk(...);                    \
        static bool const value = sizeof(chk<T>(0)) == sizeof(yes);     \
    }

위의 템플릿과 매크로는 템플릿을 인스턴스화하여 멤버 기능 포인터 유형과 실제 멤버 기능 포인터를 제공하려고합니다. 맞지 않는 유형이 있으면 sfinae는 템플릿을 무시합니다. 다음과 같은 사용 :

HAS_MEM_FUNC(toString, has_to_string);

template<typename T> void
doSomething() {
   if(has_to_string<T, std::string(T::*)()>::value) {
      ...
   } else {
      ...
   }
}

그러나 당신은 단지 그것을 부를 수는 없습니다 toString If If Branch에서 기능하십시오. 컴파일러는 두 분기의 유효성을 확인하므로 함수가 존재하지 않는 경우에는 실패합니다. 한 가지 방법은 sfinae를 다시 한 번 사용하는 것입니다 (enable_if도 부스트에서 얻을 수 있음).

template<bool C, typename T = void>
struct enable_if {
  typedef T type;
};

template<typename T>
struct enable_if<false, T> { };

HAS_MEM_FUNC(toString, has_to_string);

template<typename T> 
typename enable_if<has_to_string<T, 
                   std::string(T::*)()>::value, std::string>::type
doSomething(T * t) {
   /* something when T has toString ... */
   return t->toString();
}

template<typename T> 
typename enable_if<!has_to_string<T, 
                   std::string(T::*)()>::value, std::string>::type
doSomething(T * t) {
   /* something when T doesnt have toString ... */
   return "T::toString() does not exist.";
}

그것을 사용하는 즐거움을 누리십시오. 그것의 장점은 과부하 된 멤버 함수와 Const 멤버 기능에도 작동한다는 것입니다 (사용 기억하십시오. std::string(T::*)() const 멤버 함수 포인터 유형으로!).

이 질문은 2 살이지만 대답을 추가 할 것입니다. 바라건대, 그것은 이전의 논란의 여지가 많을 정도로 우수한 솔루션을 명확히 할 것입니다. 나는 Nicola Bonelli와 Johannes Schaub에 대한 매우 유용한 답변을 취해 IMHO, 더 읽기 쉽고 명확하며 필요하지 않은 솔루션으로 통합했습니다. typeof 확대:

template <class Type>
class TypeHasToString
{
    // This type won't compile if the second template parameter isn't of type T,
    // so I can put a function pointer type in the first parameter and the function
    // itself in the second thus checking that the function has a specific signature.
    template <typename T, T> struct TypeCheck;

    typedef char Yes;
    typedef long No;

    // A helper struct to hold the declaration of the function pointer.
    // Change it if the function signature changes.
    template <typename T> struct ToString
    {
        typedef void (T::*fptr)();
    };

    template <typename T> static Yes HasToString(TypeCheck< typename ToString<T>::fptr, &T::toString >*);
    template <typename T> static No  HasToString(...);

public:
    static bool const value = (sizeof(HasToString<Type>(0)) == sizeof(Yes));
};

GCC 4.1.2로 확인했습니다. 크레딧은 주로 Nicola Bonelli와 Johannes Schaub에게 간다. 그래서 내 대답이 당신에게 도움이되면 그들에게 투표하십시오 :)

C ++ 20- requires 표현

C ++ 20으로 개념과 다음과 같은 다양한 도구 requires 표현 기능 존재를 확인하는 내장 방법입니다. tehm을 사용하면 다시 쓸 수 있습니다 optionalToString 다음과 같이 기능 :

template<class T>
std::string optionalToString(T* obj)
{
    constexpr bool has_toString = requires(const T& t) {
        t.toString();
    };

    if constexpr (has_toString)
        return obj->toString();
    else
        return "toString not defined";
}

사전 C ++ 20- 감지 툴킷

N4502 C ++ 17 표준 라이브러리에 포함시키기 위해 검출 된 감지를 제안하여 다소 우아한 방식으로 문제를 해결할 수 있습니다. 더욱이, 그것은 방금 도서관 기초 TS v2에 받아 들여졌습니다. 그것은 몇 가지 metafintions를 포함합니다 std::is_detected 이는 상단에 유형 또는 기능 감지 메타 변환을 쉽게 작성하는 데 사용할 수 있습니다. 사용 방법은 다음과 같습니다.

template<typename T>
using toString_t = decltype( std::declval<T&>().toString() );

template<typename T>
constexpr bool has_toString = std::is_detected_v<toString_t, T>;

위의 예는 테스트되지 않았습니다. 감지 툴킷은 아직 표준 라이브러리에서 사용할 수 없지만 제안에는 실제로 필요한 경우 쉽게 복사 할 수있는 전체 구현이 포함되어 있습니다. C ++ 17 기능으로 훌륭합니다 if constexpr:

template<class T>
std::string optionalToString(T* obj)
{
    if constexpr (has_toString<T>)
        return obj->toString();
    else
        return "toString not defined";
}

boost.tti

덜 우아하지만 그러한 점검을 수행하는 또 다른 관용 툴킷은 다음과 같습니다. boost.tti, 부스트 1.54.0에 도입되었습니다. 예를 들어, 매크로를 사용해야합니다. BOOST_TTI_HAS_MEMBER_FUNCTION. 사용 방법은 다음과 같습니다.

#include <boost/tti/has_member_function.hpp>

// Generate the metafunction
BOOST_TTI_HAS_MEMBER_FUNCTION(toString)

// Check whether T has a member function toString
// which takes no parameter and returns a std::string
constexpr bool foo = has_member_function_toString<T, std::string>::value;

그런 다음 사용할 수 있습니다 bool sfinae 검사를 작성합니다.

설명

매크로 BOOST_TTI_HAS_MEMBER_FUNCTION MetaFunction을 생성합니다 has_member_function_toString 확인 된 유형을 첫 번째 템플릿 매개 변수로 사용합니다. 두 번째 템플릿 매개 변수는 멤버 함수의 리턴 유형에 해당하고 다음 매개 변수는 함수의 매개 변수 유형에 해당합니다. 회원 value 포함 true 수업이라면 T 회원 기능이 있습니다 std::string toString().

대안 적으로, has_member_function_toString 멤버 기능 포인터를 템플릿 매개 변수로 취할 수 있습니다. 따라서 교체 할 수 있습니다 has_member_function_toString<T, std::string>::value ~에 의해 has_member_function_toString<std::string T::* ()>::value.

C ++ 11을위한 간단한 솔루션 :

template<class T>
auto optionalToString(T* obj)
 -> decltype(  obj->toString()  )
{
    return     obj->toString();
}
auto optionalToString(...) -> string
{
    return "toString not defined";
}

3 년 후 업데이트 : (그리고 이것은 테스트되지 않았습니다). 존재를 테스트하기 위해 이것이 효과가 있다고 생각합니다.

template<class T>
constexpr auto test_has_toString_method(T* obj)
 -> decltype(  obj->toString() , std::true_type{} )
{
    return     obj->toString();
}
constexpr auto test_has_toString_method(...) -> std::false_type
{
    return "toString not defined";
}

이것이 유형의 특성이있는 것입니다. 불행히도, 그들은 수동으로 정의되어야합니다. 귀하의 경우 다음을 상상해보십시오.

template <typename T>
struct response_trait {
    static bool const has_tostring = false;
};

template <>
struct response_trait<your_type_with_tostring> {
    static bool const has_tostring = true;
}

글쎄,이 질문에는 이미 긴 답변 목록이 있지만 Morwenn의 의견을 강조하고 싶습니다. C ++ 17에 대한 제안이 훨씬 간단합니다. 보다 N4502 자세한 내용은 자체 포함 된 예로 다음을 고려하십시오.

이 부분은 일정한 부분이며 헤더에 넣습니다.

// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf.
template <typename...>
using void_t = void;

// Primary template handles all types not supporting the operation.
template <typename, template <typename> class, typename = void_t<>>
struct detect : std::false_type {};

// Specialization recognizes/validates only types supporting the archetype.
template <typename T, template <typename> class Op>
struct detect<T, Op, void_t<Op<T>>> : std::true_type {};

그런 다음 변수 부분이 있습니다. 여기에는 원하는 것을 지정하는 (유형, 멤버 유형, 함수, 멤버 함수 등)가 있습니다. OP의 경우 :

template <typename T>
using toString_t = decltype(std::declval<T>().toString());

template <typename T>
using has_toString = detect<T, toString_t>;

다음의 예 N4502,보다 정교한 프로브를 보여줍니다.

// Archetypal expression for assignment operation.
template <typename T>
using assign_t = decltype(std::declval<T&>() = std::declval<T const &>())

// Trait corresponding to that archetype.
template <typename T>
using is_assignable = detect<T, assign_t>;

위에서 설명한 다른 구현과 비교하여,이 구현은 상당히 간단합니다 : 감소 된 도구 세트 (void_t 그리고 detect) 충분하고, 털이 많은 매크로가 필요하지 않습니다. 게다가, 그것은보고되었다 (참조 N4502) 이전 접근법보다 측정 값이 더 효율적으로 (컴파일 타임 및 컴파일러 메모리 소비)입니다.

여기에 있습니다 라이브 예. Clang과 잘 작동하지만 불행히도 5.1 이전의 GCC 버전은 C ++ 11 표준에 대한 다른 해석을 따랐습니다. void_t 예상대로 작동하지 않습니다. Yakk는 이미 작업을 제공했습니다. 다음 정의를 사용하십시오. void_t (매개 변수 목록의 void_t는 작동하지만 반환 유형은 아닙니다.):

#if __GNUC__ < 5 && ! defined __clang__
// https://stackoverflow.com/a/28967049/1353549
template <typename...>
struct voider
{
  using type = void;
};
template <typename...Ts>
using void_t = typename voider<Ts...>::type;
#else
template <typename...>
using void_t = void;
#endif

이것은 "x를했다면 컴파일 할 것인가?"라면 일반적인 문제에 대한 C ++ 11 솔루션입니다.

template<class> struct type_sink { typedef void type; }; // consumes a type, and makes it `void`
template<class T> using type_sink_t = typename type_sink<T>::type;
template<class T, class=void> struct has_to_string : std::false_type {}; \
template<class T> struct has_to_string<
  T,
  type_sink_t< decltype( std::declval<T>().toString() ) >
>: std::true_type {};

특성 has_to_string 그렇게 has_to_string<T>::value ~이다 true 경우에만 T 방법이 있습니다 .toString 이 맥락에서 0 인수로 호출 될 수 있습니다.

다음으로 TAG Dispatching을 사용하겠습니다.

namespace details {
  template<class T>
  std::string optionalToString_helper(T* obj, std::true_type /*has_to_string*/) {
    return obj->toString();
  }
  template<class T>
  std::string optionalToString_helper(T* obj, std::false_type /*has_to_string*/) {
    return "toString not defined";
  }
}
template<class T>
std::string optionalToString(T* obj) {
  return details::optionalToString_helper( obj, has_to_string<T>{} );
}

복잡한 sfinae 표현보다 더 관리하기 쉬운 경향이 있습니다.

당신은 당신이 그것을 많이하고 있다면 매크로로 이러한 특성을 쓸 수 있지만, 그들은 비교적 간단합니다 (각각 몇 줄). 그만한 가치가 없습니다.

#define MAKE_CODE_TRAIT( TRAIT_NAME, ... ) \
template<class T, class=void> struct TRAIT_NAME : std::false_type {}; \
template<class T> struct TRAIT_NAME< T, type_sink_t< decltype( __VA_ARGS__ ) > >: std::true_type {};

위의 일은 매크로를 만드는 것입니다 MAKE_CODE_TRAIT. 원하는 특성의 이름과 유형을 테스트 할 수있는 일부 코드를 전달합니다. T. 따라서:

MAKE_CODE_TRAIT( has_to_string, std::declval<T>().toString() )

위의 특성 클래스를 만듭니다.

제쳐두고 위의 기술은 MS MS가 "Expression Sfinae"라고 부르는 것의 일부이며 2013 년 컴파일러는 꽤 어려워집니다.

C ++ 1Y에서 다음 구문이 가능합니다.

template<class T>
std::string optionalToString(T* obj) {
  return compiled_if< has_to_string >(*obj, [&](auto&& obj) {
    return obj.toString();
  }) *compiled_else ([&]{ 
    return "toString not defined";
  });
}

이는 많은 C ++ 기능을 남용하는 인라인 컴파일 조건부 지점입니다. (코드가 인라인이라는 혜택)는 비용의 가치가 없기 때문에 (그 방법이 어떻게 작동하는지 이해하는 사람 옆에있는 옆에있는 것), 위의 솔루션의 존재가 관심을 가질 수 있기 때문에 그렇게하는 것은 그만한 가치가 없을 것입니다.

여기에는 몇 가지 사용 스 니펫이 있습니다. *이 모든 것에 대한 내장은 더 멀어졌습니다.

회원을 확인하십시오 x 주어진 수업에서. Var, Func, Class, Union 또는 Enum 일 수 있습니다.

CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;

회원 기능을 확인하십시오 void x():

//Func signature MUST have T as template variable here... simpler this way :\
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x);
bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value;

회원 변수를 확인하십시오 x:

CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;

회원 수업을 확인하십시오 x:

CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;

회원 노조를 확인하십시오 x:

CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;

회원 열거를 확인하십시오 x:

CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;

멤버 기능을 확인하십시오 x 서명에 관계없이 :

CREATE_MEMBER_CHECK(x);
CREATE_MEMBER_VAR_CHECK(x);
CREATE_MEMBER_CLASS_CHECK(x);
CREATE_MEMBER_UNION_CHECK(x);
CREATE_MEMBER_ENUM_CHECK(x);
CREATE_MEMBER_FUNC_CHECK(x);
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

또는

CREATE_MEMBER_CHECKS(x);  //Just stamps out the same macro calls as above.
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

세부 사항 및 핵심 :

/*
    - Multiple inheritance forces ambiguity of member names.
    - SFINAE is used to make aliases to member names.
    - Expression SFINAE is used in just one generic has_member that can accept
      any alias we pass it.
*/

//Variadic to force ambiguity of class members.  C++11 and up.
template <typename... Args> struct ambiguate : public Args... {};

//Non-variadic version of the line above.
//template <typename A, typename B> struct ambiguate : public A, public B {};

template<typename A, typename = void>
struct got_type : std::false_type {};

template<typename A>
struct got_type<A> : std::true_type {
    typedef A type;
};

template<typename T, T>
struct sig_check : std::true_type {};

template<typename Alias, typename AmbiguitySeed>
struct has_member {
    template<typename C> static char ((&f(decltype(&C::value))))[1];
    template<typename C> static char ((&f(...)))[2];

    //Make sure the member name is consistently spelled the same.
    static_assert(
        (sizeof(f<AmbiguitySeed>(0)) == 1)
        , "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified."
    );

    static bool const value = sizeof(f<Alias>(0)) == 2;
};

매크로 (El Diablo!) :

create_member_check :

//Check for any member with given name, whether var, func, class, union, enum.
#define CREATE_MEMBER_CHECK(member)                                         \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct Alias_##member;                                                      \
                                                                            \
template<typename T>                                                        \
struct Alias_##member <                                                     \
    T, std::integral_constant<bool, got_type<decltype(&T::member)>::value>  \
> { static const decltype(&T::member) value; };                             \
                                                                            \
struct AmbiguitySeed_##member { char member; };                             \
                                                                            \
template<typename T>                                                        \
struct has_member_##member {                                                \
    static const bool value                                                 \
        = has_member<                                                       \
            Alias_##member<ambiguate<T, AmbiguitySeed_##member>>            \
            , Alias_##member<AmbiguitySeed_##member>                        \
        >::value                                                            \
    ;                                                                       \
}

create_member_var_check :

//Check for member variable with given name.
#define CREATE_MEMBER_VAR_CHECK(var_name)                                   \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_var_##var_name : std::false_type {};                      \
                                                                            \
template<typename T>                                                        \
struct has_member_var_##var_name<                                           \
    T                                                                       \
    , std::integral_constant<                                               \
        bool                                                                \
        , !std::is_member_function_pointer<decltype(&T::var_name)>::value   \
    >                                                                       \
> : std::true_type {}

create_member_func_sig_check :

//Check for member function with given name AND signature.
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix)    \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_func_##templ_postfix : std::false_type {};                \
                                                                            \
template<typename T>                                                        \
struct has_member_func_##templ_postfix<                                     \
    T, std::integral_constant<                                              \
        bool                                                                \
        , sig_check<func_sig, &T::func_name>::value                         \
    >                                                                       \
> : std::true_type {}

create_member_class_check :

//Check for member class with given name.
#define CREATE_MEMBER_CLASS_CHECK(class_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_class_##class_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_class_##class_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_class<                                    \
            typename got_type<typename T::class_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

create_member_union_check :

//Check for member union with given name.
#define CREATE_MEMBER_UNION_CHECK(union_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_union_##union_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_union_##union_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_union<                                    \
            typename got_type<typename T::union_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

create_member_enum_check :

//Check for member enum with given name.
#define CREATE_MEMBER_ENUM_CHECK(enum_name)                 \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_enum_##enum_name : std::false_type {};    \
                                                            \
template<typename T>                                        \
struct has_member_enum_##enum_name<                         \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_enum<                                     \
            typename got_type<typename T::enum_name>::type  \
        >::value                                            \
    >                                                       \
> : std::true_type {}

create_member_func_check :

//Check for function with given name, any signature.
#define CREATE_MEMBER_FUNC_CHECK(func)          \
template<typename T>                            \
struct has_member_func_##func {                 \
    static const bool value                     \
        = has_member_##func<T>::value           \
        && !has_member_var_##func<T>::value     \
        && !has_member_class_##func<T>::value   \
        && !has_member_union_##func<T>::value   \
        && !has_member_enum_##func<T>::value    \
    ;                                           \
}

create_member_checks :

//Create all the checks for one member.  Does NOT include func sig checks.
#define CREATE_MEMBER_CHECKS(member)    \
CREATE_MEMBER_CHECK(member);            \
CREATE_MEMBER_VAR_CHECK(member);        \
CREATE_MEMBER_CLASS_CHECK(member);      \
CREATE_MEMBER_UNION_CHECK(member);      \
CREATE_MEMBER_ENUM_CHECK(member);       \
CREATE_MEMBER_FUNC_CHECK(member)

LITB에 의해 여기에 제시된 표준 C ++ 솔루션은 기본 클래스에서 방법이 정의 된 경우 예상대로 작동하지 않습니다.

이 상황을 처리하는 솔루션은 다음을 참조하십시오.

러시아어 :http://www.rsdn.ru/forum/message/2759773.1.aspx

Roman.Perepelitsa의 영어 번역 :http://groups.google.com/group/comp.lang.c++.moderated/tree/browse_frm/thread/4f7c7a96f9afbe44/c95a7b4c645e449f?pli=1

그것은 미친 듯이 영리합니다. 그러나이 솔루티온의 한 가지 문제는 테스트중인 유형이 기본 클래스 (예 : 원시 유형)로 사용할 수없는 경우 컴파일러 오류를 제공한다는 것입니다.

Visual Studio에서는 인수가없는 메소드로 작업하는 경우 표현식 크기에서 ()을 추론하기 위해 인수 주위에 추가 중복 () 쌍을 삽입해야한다는 것을 알았습니다.

msvc에는 __if_exists와 __if_not_exists 키워드가 있습니다.문서). Nicola의 Typeof-Sfinae 접근법과 함께 OP와 같은 GCC 및 MSVC에 대한 점검을 만들 수있었습니다.

업데이트: 소스를 찾을 수 있습니다 여기

위의 솔루션과 달리 상속 된 멤버 기능도 확인하는 다른 스레드에서 이것에 대한 답을 썼습니다.

상속 된 멤버 기능을 확인하는 Sfinae

다음은 해당 솔루션의 몇 가지 예입니다.

1 : 예제 1 : 예제 1 : 예제 1 : : 예제 1 : : 예제 1 : : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제 1 : 예제.

우리는 다음 서명이있는 회원을 확인하고 있습니다.T::const_iterator begin() const

template<class T> struct has_const_begin
{
    typedef char (&Yes)[1];
    typedef char (&No)[2];

    template<class U> 
    static Yes test(U const * data, 
                    typename std::enable_if<std::is_same<
                             typename U::const_iterator, 
                             decltype(data->begin())
                    >::value>::type * = 0);
    static No test(...);
    static const bool value = sizeof(Yes) == sizeof(has_const_begin::test((typename std::remove_reference<T>::type*)0));
};

방법의 콘트니스를 확인하고 원시 유형과도 작동합니다. (내말은 has_const_begin<int>::value 거짓이고 컴파일 타임 오류를 일으키지 않습니다.)

예 2

이제 우리는 서명을 찾고 있습니다. void foo(MyClass&, unsigned)

template<class T> struct has_foo
{
    typedef char (&Yes)[1];
    typedef char (&No)[2];

    template<class U>
    static Yes test(U * data, MyClass* arg1 = 0,
                    typename std::enable_if<std::is_void<
                             decltype(data->foo(*arg1, 1u))
                    >::value>::type * = 0);
    static No test(...);
    static const bool value = sizeof(Yes) == sizeof(has_foo::test((typename std::remove_reference<T>::type*)0));
};

MyClass는 기본 구성 가능이거나 특수 개념을 만족시키기 위해 기본적으로 필요하지 않습니다. 이 기술은 템플릿 멤버와도 작동합니다.

나는 이것에 관한 의견을 간절히 기다리고 있습니다.

이제 이것은 A였습니다 멋진 작은 퍼즐 - 좋은 질문!

다음은 대안입니다 Nicola Bonelli의 해결책 그것은 비표준에 의존하지 않습니다 typeof 운영자.

불행히도 GCC (MingW) 3.4.5 또는 Digital Mars 8.42N에서는 작동하지 않지만 모든 버전의 MSVC (VC6 포함) 및 COMEAU C ++에서 작동합니다.

더 긴 주석 블록에는 작동 방식 (또는 작동하는 방법)에 대한 세부 사항이 있습니다. 말하듯이, 어떤 행동이 표준을 준수하는지 잘 모르겠습니다. 나는 그것에 대한 논평을 환영합니다.


업데이트 -2008 년 11 월 7 일 :

이 코드는 구문 적으로 정확하지만 MSVC와 Comeau C ++ 쇼가 표준을 따르지 않는 동작 (감사합니다. 레온 티머 맨 그리고 litb 나를 올바른 방향으로 가리키기 위해). C ++ 03 표준은 다음을 말합니다.

14.6.2 종속 이름 [temp.dep

단락 3

클래스 템플릿 또는 클래스 템플릿 멤버의 정의에서 클래스 템플릿의 기본 클래스가 템플릿 패러라미터에 의존하는 경우, 클래스 정의의 지점에서 자격이없는 이름 조회 중에 기본 클래스 범위가 검사되지 않습니다. 템플릿 또는 멤버 또는 클래스 템플릿 또는 멤버의 인스턴스화 중.

따라서 MSVC 또는 Comeau가 toString() 회원 기능 T 통화 사이트에서 이름 조회를 수행합니다 doToString() 템플릿이 인스턴스화되면 틀 렸습니다 (실제로이 경우 내가 찾던 동작이지만).

GCC와 디지털 화성의 동작은 정확 해 보입니다. 두 경우 모두 비회원 toString() 함수는 통화에 바인딩됩니다.

쥐 - 나는 영리한 솔루션을 찾았을 것이라고 생각했고 대신 몇 가지 컴파일러 버그를 발견했습니다 ...


#include <iostream>
#include <string>

struct Hello
{
    std::string toString() {
        return "Hello";
    }
};

struct Generic {};


// the following namespace keeps the toString() method out of
//  most everything - except the other stuff in this
//  compilation unit

namespace {
    std::string toString()
    {
        return "toString not defined";
    }

    template <typename T>
    class optionalToStringImpl : public T
    {
    public:
        std::string doToString() {

            // in theory, the name lookup for this call to 
            //  toString() should find the toString() in 
            //  the base class T if one exists, but if one 
            //  doesn't exist in the base class, it'll 
            //  find the free toString() function in 
            //  the private namespace.
            //
            // This theory works for MSVC (all versions
            //  from VC6 to VC9) and Comeau C++, but
            //  does not work with MinGW 3.4.5 or 
            //  Digital Mars 8.42n
            //
            // I'm honestly not sure what the standard says 
            //  is the correct behavior here - it's sort 
            //  of like ADL (Argument Dependent Lookup - 
            //  also known as Koenig Lookup) but without
            //  arguments (except the implied "this" pointer)

            return toString();
        }
    };
}

template <typename T>
std::string optionalToString(T & obj)
{
    // ugly, hacky cast...
    optionalToStringImpl<T>* temp = reinterpret_cast<optionalToStringImpl<T>*>( &obj);

    return temp->doToString();
}



int
main(int argc, char *argv[])
{
    Hello helloObj;
    Generic genericObj;

    std::cout << optionalToString( helloObj) << std::endl;
    std::cout << optionalToString( genericObj) << std::endl;
    return 0;
}

제공된 솔루션을 수정했습니다 https://stackoverflow.com/a/264088/2712152 좀 더 일반적인 것입니다. 또한 새로운 C ++ 11 기능을 사용하지 않기 때문에 오래된 컴파일러와 함께 사용할 수 있으며 MSVC 와도 작동해야합니다. 그러나 컴파일러는 C99가 변수 매크로를 사용하므로 C99를 사용할 수 있어야합니다.

다음 매크로를 사용하여 특정 클래스에 특정 TypEdef가 있는지 확인할 수 있습니다.

/** 
 * @class      : HAS_TYPEDEF
 * @brief      : This macro will be used to check if a class has a particular
 * typedef or not.
 * @param typedef_name : Name of Typedef
 * @param name  : Name of struct which is going to be run the test for
 * the given particular typedef specified in typedef_name
 */
#define HAS_TYPEDEF(typedef_name, name)                           \
   template <typename T>                                          \
   struct name {                                                  \
      typedef char yes[1];                                        \
      typedef char no[2];                                         \
      template <typename U>                                       \
      struct type_check;                                          \
      template <typename _1>                                      \
      static yes& chk(type_check<typename _1::typedef_name>*);    \
      template <typename>                                         \
      static no& chk(...);                                        \
      static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
   }

다음 매크로는 특정 클래스에 특정 멤버 함수가 있는지 확인하는 데 주어진 수의 인수가 있는지 확인할 수 있습니다.

/** 
 * @class      : HAS_MEM_FUNC
 * @brief      : This macro will be used to check if a class has a particular
 * member function implemented in the public section or not. 
 * @param func : Name of Member Function
 * @param name : Name of struct which is going to be run the test for
 * the given particular member function name specified in func
 * @param return_type: Return type of the member function
 * @param ellipsis(...) : Since this is macro should provide test case for every
 * possible member function we use variadic macros to cover all possibilities
 */
#define HAS_MEM_FUNC(func, name, return_type, ...)                \
   template <typename T>                                          \
   struct name {                                                  \
      typedef return_type (T::*Sign)(__VA_ARGS__);                \
      typedef char yes[1];                                        \
      typedef char no[2];                                         \
      template <typename U, U>                                    \
      struct type_check;                                          \
      template <typename _1>                                      \
      static yes& chk(type_check<Sign, &_1::func>*);              \
      template <typename>                                         \
      static no& chk(...);                                        \
      static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
   }

위의 2 개의 매크로를 사용하여 has_typedef 및 has_mem_func에 대한 검사를 수행 할 수 있습니다.

class A {
public:
  typedef int check;
  void check_function() {}
};

class B {
public:
  void hello(int a, double b) {}
  void hello() {}
};

HAS_MEM_FUNC(check_function, has_check_function, void, void);
HAS_MEM_FUNC(hello, hello_check, void, int, double);
HAS_MEM_FUNC(hello, hello_void_check, void, void);
HAS_TYPEDEF(check, has_typedef_check);

int main() {
  std::cout << "Check Function A:" << has_check_function<A>::value << std::endl;
  std::cout << "Check Function B:" << has_check_function<B>::value << std::endl;
  std::cout << "Hello Function A:" << hello_check<A>::value << std::endl;
  std::cout << "Hello Function B:" << hello_check<B>::value << std::endl;
  std::cout << "Hello void Function A:" << hello_void_check<A>::value << std::endl;
  std::cout << "Hello void Function B:" << hello_void_check<B>::value << std::endl;
  std::cout << "Check Typedef A:" << has_typedef_check<A>::value << std::endl;
  std::cout << "Check Typedef B:" << has_typedef_check<B>::value << std::endl;
}

sfinae 및 템플릿 부분 전문화를 사용하는 예. Has_foo 개념 확인 :

#include <type_traits>
struct A{};

struct B{ int foo(int a, int b);};

struct C{void foo(int a, int b);};

struct D{int foo();};

struct E: public B{};

// available in C++17 onwards as part of <type_traits>
template<typename...>
using void_t = void;

template<typename T, typename = void> struct Has_foo: std::false_type{};

template<typename T> 
struct Has_foo<T, void_t<
    std::enable_if_t<
        std::is_same<
            int, 
            decltype(std::declval<T>().foo((int)0, (int)0))
        >::value
    >
>>: std::true_type{};


static_assert(not Has_foo<A>::value, "A does not have a foo");
static_assert(Has_foo<B>::value, "B has a foo");
static_assert(not Has_foo<C>::value, "C has a foo with the wrong return. ");
static_assert(not Has_foo<D>::value, "D has a foo with the wrong arguments. ");
static_assert(Has_foo<E>::value, "E has a foo since it inherits from B");

이상한 사람은이 사이트에서 한 번 본 다음과 같은 멋진 트릭을 제안하지 않았습니다.

template <class T>
struct has_foo
{
    struct S { void foo(...); };
    struct derived : S, T {};

    template <typename V, V> struct W {};

    template <typename X>
    char (&test(W<void (X::*)(), &X::foo> *))[1];

    template <typename>
    char (&test(...))[2];

    static const bool value = sizeof(test<derived>(0)) == 1;
};

당신은 t가 수업인지 확인해야합니다. FOO 조회에서 모호성은 대체 실패 인 것 같습니다. 그래도 GCC에서 작동하게했지만 표준인지 확실하지 않습니다.

일부 "기능"이 유형별로 지원되는지 확인하는 데 사용할 수있는 일반 템플릿.

#include <type_traits>

template <template <typename> class TypeChecker, typename Type>
struct is_supported
{
    // these structs are used to recognize which version
    // of the two functions was chosen during overload resolution
    struct supported {};
    struct not_supported {};

    // this overload of chk will be ignored by SFINAE principle
    // if TypeChecker<Type_> is invalid type
    template <typename Type_>
    static supported chk(typename std::decay<TypeChecker<Type_>>::type *);

    // ellipsis has the lowest conversion rank, so this overload will be
    // chosen during overload resolution only if the template overload above is ignored
    template <typename Type_>
    static not_supported chk(...);

    // if the template overload of chk is chosen during
    // overload resolution then the feature is supported
    // if the ellipses overload is chosen the the feature is not supported
    static constexpr bool value = std::is_same<decltype(chk<Type>(nullptr)),supported>::value;
};

메소드가 있는지 확인하는 템플릿 foo 그것은 서명과 호환됩니다 double(const char*)

// if T doesn't have foo method with the signature that allows to compile the bellow
// expression then instantiating this template is Substitution Failure (SF)
// which Is Not An Error (INAE) if this happens during overload resolution
template <typename T>
using has_foo = decltype(double(std::declval<T>().foo(std::declval<const char*>())));

// types that support has_foo
struct struct1 { double foo(const char*); };            // exact signature match
struct struct2 { int    foo(const std::string &str); }; // compatible signature
struct struct3 { float  foo(...); };                    // compatible ellipsis signature
struct struct4 { template <typename T>
                 int    foo(T t); };                    // compatible template signature

// types that do not support has_foo
struct struct5 { void        foo(const char*); }; // returns void
struct struct6 { std::string foo(const char*); }; // std::string can't be converted to double
struct struct7 { double      foo(      int *); }; // const char* can't be converted to int*
struct struct8 { double      bar(const char*); }; // there is no foo method

int main()
{
    std::cout << std::boolalpha;

    std::cout << is_supported<has_foo, int    >::value << std::endl; // false
    std::cout << is_supported<has_foo, double >::value << std::endl; // false

    std::cout << is_supported<has_foo, struct1>::value << std::endl; // true
    std::cout << is_supported<has_foo, struct2>::value << std::endl; // true
    std::cout << is_supported<has_foo, struct3>::value << std::endl; // true
    std::cout << is_supported<has_foo, struct4>::value << std::endl; // true

    std::cout << is_supported<has_foo, struct5>::value << std::endl; // false
    std::cout << is_supported<has_foo, struct6>::value << std::endl; // false
    std::cout << is_supported<has_foo, struct7>::value << std::endl; // false
    std::cout << is_supported<has_foo, struct8>::value << std::endl; // false

    return 0;
}

http://coliru.stacked-crooked.com/a/83c6a631ed42cea4

여기에는 많은 답이 있지만, 나는 버전을 찾지 못했습니다. 진짜 메소드 해상도 순서, 최신 C ++ 기능을 사용하지 않으면 서 (C ++ 98 기능 만 사용).
참고 :이 버전은 VC ++ 2013, G ++ 5.2.0 및 Onlline 컴파일러로 테스트하고 작동합니다.

그래서 나는 sizeof () 만 사용하는 버전을 생각해 냈습니다.

template<typename T> T declval(void);

struct fake_void { };
template<typename T> T &operator,(T &,fake_void);
template<typename T> T const &operator,(T const &,fake_void);
template<typename T> T volatile &operator,(T volatile &,fake_void);
template<typename T> T const volatile &operator,(T const volatile &,fake_void);

struct yes { char v[1]; };
struct no  { char v[2]; };
template<bool> struct yes_no:yes{};
template<> struct yes_no<false>:no{};

template<typename T>
struct has_awesome_member {
 template<typename U> static yes_no<(sizeof((
   declval<U>().awesome_member(),fake_void()
  ))!=0)> check(int);
 template<typename> static no check(...);
 enum{value=sizeof(check<T>(0)) == sizeof(yes)};
};


struct foo { int awesome_member(void); };
struct bar { };
struct foo_void { void awesome_member(void); };
struct wrong_params { void awesome_member(int); };

static_assert(has_awesome_member<foo>::value,"");
static_assert(!has_awesome_member<bar>::value,"");
static_assert(has_awesome_member<foo_void>::value,"");
static_assert(!has_awesome_member<wrong_params>::value,"");

라이브 데모 (확장 된 반환 유형 확인 및 VC ++ 2010 해결 방법) : http://cpp.sh/5b2vs

내가 직접 생각해 보았을 때 출처는 없습니다.

G ++ 컴파일러에서 라이브 데모를 실행할 때 0의 배열 크기가 허용되므로 사용 된 STATIC_ASSERT는 실패하더라도 컴파일러 오류를 트리거하지 않습니다.
일반적으로 사용되는 작업은 매크로의 'typedef'를 'extern'으로 바꾸는 것입니다.

이 솔루션은 어떻습니까?

#include <type_traits>

template <typename U, typename = void> struct hasToString : std::false_type { };

template <typename U>
struct hasToString<U,
  typename std::enable_if<bool(sizeof(&U::toString))>::type
> : std::true_type { };

다음은 템플릿 멤버 기능을 포함하여 기본 인수가 포함 된 모든 가능한 모든 멤버 기능 과부하를 처리하는 내 버전입니다. 주어진 ARG 유형과 함께 멤버 기능을 일부 클래스 유형으로 호출 할 때 3 개의 상호 배타적 시나리오를 구별합니다. 예제 사용 :

#include <string>
#include <vector>

HAS_MEM(bar)
HAS_MEM_FUN_CALL(bar)

struct test
{
   void bar(int);
   void bar(double);
   void bar(int,double);

   template < typename T >
   typename std::enable_if< not std::is_integral<T>::value >::type
   bar(const T&, int=0){}

   template < typename T >
   typename std::enable_if< std::is_integral<T>::value >::type
   bar(const std::vector<T>&, T*){}

   template < typename T >
   int bar(const std::string&, int){}
};

이제 다음과 같이 사용할 수 있습니다.

int main(int argc, const char * argv[])
{
   static_assert( has_mem_bar<test>::value , "");

   static_assert( has_valid_mem_fun_call_bar<test(char const*,long)>::value , "");
   static_assert( has_valid_mem_fun_call_bar<test(std::string&,long)>::value , "");

   static_assert( has_valid_mem_fun_call_bar<test(std::vector<int>, int*)>::value , "");
   static_assert( has_no_viable_mem_fun_call_bar<test(std::vector<double>, double*)>::value , "");

   static_assert( has_valid_mem_fun_call_bar<test(int)>::value , "");
   static_assert( std::is_same<void,result_of_mem_fun_call_bar<test(int)>::type>::value , "");

   static_assert( has_valid_mem_fun_call_bar<test(int,double)>::value , "");
   static_assert( not has_valid_mem_fun_call_bar<test(int,double,int)>::value , "");

   static_assert( not has_ambiguous_mem_fun_call_bar<test(double)>::value , "");
   static_assert( has_ambiguous_mem_fun_call_bar<test(unsigned)>::value , "");

   static_assert( has_viable_mem_fun_call_bar<test(unsigned)>::value , "");
   static_assert( has_viable_mem_fun_call_bar<test(int)>::value , "");

   static_assert( has_no_viable_mem_fun_call_bar<test(void)>::value , "");

   return 0;
}

다음은 C ++ 11로 작성된 코드입니다. 그러나 C ++ 11으로 작성된 코드는 다음과 같습니다. 그러나 확장 유형 (예 : GCC)이있는 비 C ++ 11으로 쉽게 포팅 할 수 있습니다. has_mem 매크로를 자신의 것으로 바꿀 수 있습니다.

#pragma once

#if __cplusplus >= 201103

#include <utility>
#include <type_traits>

#define HAS_MEM(mem)                                                                                     \
                                                                                                     \
template < typename T >                                                                               \
struct has_mem_##mem                                                                                  \
{                                                                                                     \
  struct yes {};                                                                                     \
  struct no  {};                                                                                     \
                                                                                                     \
  struct ambiguate_seed { char mem; };                                                               \
  template < typename U > struct ambiguate : U, ambiguate_seed {};                                   \
                                                                                                     \
  template < typename U, typename = decltype(&U::mem) > static constexpr no  test(int);              \
  template < typename                                 > static constexpr yes test(...);              \
                                                                                                     \
  static bool constexpr value = std::is_same<decltype(test< ambiguate<T> >(0)),yes>::value ;         \
  typedef std::integral_constant<bool,value>    type;                                                \
};


#define HAS_MEM_FUN_CALL(memfun)                                                                         \
                                                                                                     \
template < typename Signature >                                                                       \
struct has_valid_mem_fun_call_##memfun;                                                               \
                                                                                                     \
template < typename T, typename... Args >                                                             \
struct has_valid_mem_fun_call_##memfun< T(Args...) >                                                  \
{                                                                                                     \
  struct yes {};                                                                                     \
  struct no  {};                                                                                     \
                                                                                                     \
  template < typename U, bool = has_mem_##memfun<U>::value >                                         \
  struct impl                                                                                        \
  {                                                                                                  \
     template < typename V, typename = decltype(std::declval<V>().memfun(std::declval<Args>()...)) > \
     struct test_result { using type = yes; };                                                       \
                                                                                                     \
     template < typename V > static constexpr typename test_result<V>::type test(int);               \
     template < typename   > static constexpr                            no test(...);               \
                                                                                                     \
     static constexpr bool value = std::is_same<decltype(test<U>(0)),yes>::value;                    \
     using type = std::integral_constant<bool, value>;                                               \
  };                                                                                                 \
                                                                                                     \
  template < typename U >                                                                            \
  struct impl<U,false> : std::false_type {};                                                         \
                                                                                                     \
  static constexpr bool value = impl<T>::value;                                                      \
  using type = std::integral_constant<bool, value>;                                                  \
};                                                                                                    \
                                                                                                     \
template < typename Signature >                                                                       \
struct has_ambiguous_mem_fun_call_##memfun;                                                           \
                                                                                                     \
template < typename T, typename... Args >                                                             \
struct has_ambiguous_mem_fun_call_##memfun< T(Args...) >                                              \
{                                                                                                     \
  struct ambiguate_seed { void memfun(...); };                                                       \
                                                                                                     \
  template < class U, bool = has_mem_##memfun<U>::value >                                            \
  struct ambiguate : U, ambiguate_seed                                                               \
  {                                                                                                  \
    using ambiguate_seed::memfun;                                                                    \
    using U::memfun;                                                                                 \
  };                                                                                                 \
                                                                                                     \
  template < class U >                                                                               \
  struct ambiguate<U,false> : ambiguate_seed {};                                                     \
                                                                                                     \
  static constexpr bool value = not has_valid_mem_fun_call_##memfun< ambiguate<T>(Args...) >::value; \
  using type = std::integral_constant<bool, value>;                                                  \
};                                                                                                    \
                                                                                                     \
template < typename Signature >                                                                       \
struct has_viable_mem_fun_call_##memfun;                                                              \
                                                                                                     \
template < typename T, typename... Args >                                                             \
struct has_viable_mem_fun_call_##memfun< T(Args...) >                                                 \
{                                                                                                     \
  static constexpr bool value = has_valid_mem_fun_call_##memfun<T(Args...)>::value                   \
                             or has_ambiguous_mem_fun_call_##memfun<T(Args...)>::value;              \
  using type = std::integral_constant<bool, value>;                                                  \
};                                                                                                    \
                                                                                                     \
template < typename Signature >                                                                       \
struct has_no_viable_mem_fun_call_##memfun;                                                           \
                                                                                                     \
template < typename T, typename... Args >                                                             \
struct has_no_viable_mem_fun_call_##memfun < T(Args...) >                                             \
{                                                                                                     \
  static constexpr bool value = not has_viable_mem_fun_call_##memfun<T(Args...)>::value;             \
  using type = std::integral_constant<bool, value>;                                                  \
};                                                                                                    \
                                                                                                     \
template < typename Signature >                                                                       \
struct result_of_mem_fun_call_##memfun;                                                               \
                                                                                                     \
template < typename T, typename... Args >                                                             \
struct result_of_mem_fun_call_##memfun< T(Args...) >                                                  \
{                                                                                                     \
  using type = decltype(std::declval<T>().memfun(std::declval<Args>()...));                          \
};

#endif

C ++ 14에서 모든 메타 프로 그램을 건너 뛰고 사용하기 만하면됩니다. fit::conditional ~로부터 맞다 도서관:

template<class T>
std::string optionalToString(T* x)
{
    return fit::conditional(
        [](auto* obj) -> decltype(obj->toString()) { return obj->toString(); },
        [](auto*) { return "toString not defined"; }
    )(x);
}

Lambdas에서 직접 기능을 만들 수도 있습니다.

FIT_STATIC_LAMBDA_FUNCTION(optionalToString) = fit::conditional(
    [](auto* obj) -> decltype(obj->toString(), std::string()) { return obj->toString(); },
    [](auto*) -> std::string { return "toString not defined"; }
);

그러나 일반 람다를 지원하지 않는 컴파일러를 사용하는 경우 별도의 기능 개체를 작성해야합니다.

struct withToString
{
    template<class T>
    auto operator()(T* obj) const -> decltype(obj->toString(), std::string())
    {
        return obj->toString();
    }
};

struct withoutToString
{
    template<class T>
    std::string operator()(T*) const
    {
        return "toString not defined";
    }
};

FIT_STATIC_FUNCTION(optionalToString) = fit::conditional(
    withToString(),
    withoutToString()
);

다음은 작업 코드의 예입니다.

template<typename T>
using toStringFn = decltype(std::declval<const T>().toString());

template <class T, toStringFn<T>* = nullptr>
std::string optionalToString(const T* obj, int)
{
    return obj->toString();
}

template <class T>
std::string optionalToString(const T* obj, long)
{
    return "toString not defined";
}

int main()
{
    A* a;
    B* b;

    std::cout << optionalToString(a, 0) << std::endl; // This is A
    std::cout << optionalToString(b, 0) << std::endl; // toString not defined
}

toStringFn<T>* = nullptr 추가 기능을 사용하는 기능을 활성화합니다 int 기능보다 우선 순위를 가진 인수 long 호출했을 때 0.

반환하는 함수에 대해 동일한 원리를 사용할 수 있습니다. true 함수가 구현 된 경우.

template <typename T>
constexpr bool toStringExists(long)
{
    return false;
}

template <typename T, toStringFn<T>* = nullptr>
constexpr bool toStringExists(int)
{
    return true;
}


int main()
{
    A* a;
    B* b;

    std::cout << toStringExists<A>(0) << std::endl; // true
    std::cout << toStringExists<B>(0) << std::endl; // false
}

비슷한 문제가있었습니다.

소수의 기본 클래스에서 파생 될 수있는 템플릿 클래스, 일부 구성원이 있고 다른 멤버가없는 일부는 그렇지 않은 템플릿 클래스입니다.

나는 "Typeof"(Nicola Bonelli의) 대답과 유사하게 해결했지만 decltype를 사용하여 MSV에서 올바르게 컴파일하고 실행됩니다.

#include <iostream>
#include <string>

struct Generic {};    
struct HasMember 
{
  HasMember() : _a(1) {};
  int _a;
};    

// SFINAE test
template <typename T>
class S : public T
{
public:
  std::string foo (std::string b)
  {
    return foo2<T>(b,0);
  }

protected:
  template <typename T> std::string foo2 (std::string b, decltype (T::_a))
  {
    return b + std::to_string(T::_a);
  }
  template <typename T> std::string foo2 (std::string b, ...)
  {
    return b + "No";
  }
};

int main(int argc, char *argv[])
{
  S<HasMember> d1;
  S<Generic> d2;

  std::cout << d1.foo("HasMember: ") << std::endl;
  std::cout << d2.foo("Generic: ") << std::endl;
  return 0;
}
template<class T>
auto optionalToString(T* obj)
->decltype( obj->toString(), std::string() )
{
     return obj->toString();
}

template<class T>
auto optionalToString(T* obj)
->decltype( std::string() )
{
     throw "Error!";
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top