2012-07-04 4 views
8

전문 템플릿 클래스 선택하지 않는 "특별한"전문 펑 인쇄를 선택하려면 다음 코드에서SFINAE : 컴파일러는 내가 <a href="http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error" rel="noreferrer">SFINAE</a> 문제가

, 나는 C + +를 원하는 컴파일러를하지만, 일반적으로 "인쇄하는 것 "대신.

#include <iostream> 
#include <vector> 

template<class T, class V = void> 
struct Functor { 
    void operator()() const { 
    std::cerr << "general" << std::endl; 
    } 
}; 

template<class T> 
struct Functor<T, typename T::Vec> { 
    void operator()() const { 
    std::cerr << "special" << std::endl; 
    } 
}; 

struct Foo { 
    typedef std::vector<int> Vec; 
}; 

int main() { 
    Functor<Foo> ac; 
    ac(); 
} 

특수 구조체가 자동으로 사용되도록 수정하려면 어떻게해야합니까? 참고 Functor 구조체를 Foo에 직접 지정하고 싶지는 않지만 Vec 유형의 모든 유형에 특수화하고 싶습니다. : 내가 마지막 대답을 오해의 소지에 대한 4.4.4

+0

'compiler' 태그를 제거했습니다. 일반적으로 컴파일 프로세스 자체에 관한 질문에 사용되는 반면,이 질문은 C++ 언어에 관한 것입니다. –

답변

11

죄송 ++ g을 사용하고

P.S, 나는 그것을 간단하게 될 것입니다 잠시 생각했다. 그래서 여기서 완전한 해결책을 제공하려고 노력할 것입니다.

형질

: 일반적인 접근 방식은 문제의이 유형은 특성 도우미 템플릿을 작성하고 수업 전문성을 결정하는 (C++ 11 부스트 또는 수동 구현 중) enable_if와 함께 사용하는 것입니다 해결하기 위해 ,

template <typename T> 
struct has_nested_Vec { 
    typedef char yes; 
    typedef char (&no)[2]; 
    template <typename U> 
    static yes test(typename U::Vec* p); 
    template <typename U> 
    static no test(...); 

    static const bool value = sizeof(test<T>(0)) == sizeof(yes); 
}; 

접근 방식은 간단하다 두 템플릿 함수, 다양한 크기의 형태를 돌려 제공 : 쓰기

간단한 방법, 반드시 최선을하지만 간단하다. 그 중 하나는 중첩 된 Vec 형식을 사용하고 다른 하나는 줄임표를 사용합니다. 중첩 된 Vec이있는 모든 유형의 경우 첫 번째 오버로드가 더 적합합니다 (줄임표는 모든 유형에 대해 최악의 일치입니다). Vec SFINAE가 중첩되지 않는 유형의 경우에는 해당 과부하를 제거하고 왼쪽 옵션 만 줄임표가됩니다. 이제 우리는 유형이 중첩 된 Vec 유형을 가지고 있는지 여부를 묻는 특성을 가지고 있습니다.

당신은 어떤 라이브러리에서이를 사용할 수 있는지

이 사용하거나 자신의 롤 수 있습니다, 그것은 매우 간단합니다 :

template <bool state, typename T = void> 
struct enable_if {}; 

template <typename T> 
struct enable_if<true,T> { 
    typedef T type; 
}; 

첫 번째 인수가 false이며, 기본 템플릿입니다 유일한 옵션이며 중첩 된 type이없는 경우 조건이 true 인 경우 enable_if에는 SFINAE에서 사용할 수있는 중첩 된 type이 있습니다.

구현

이제 우리는 템플릿과 중첩 Vec으로 만 유형 SFINAE를 사용하는 전문화를 제공해야합니다 : 우리는 유형 Functor를 인스턴스화 할 때마다

template<class T, class V = void> 
struct Functor { 
    void operator()() const { 
     std::cerr << "general" << std::endl; 
    } 
}; 
template<class T> 
struct Functor<T, typename enable_if<has_nested_Vec<T>::value>::type > { 
    void operator()() const { 
     std::cerr << "special" << std::endl; 
    } 
}; 

의 컴파일러는 has_nested_Vec을 인스턴스화하고 enable_if에 전달 된 진리 값을 얻는 전문화를 사용하려고합니다. 값이 false 인 유형의 경우 enable_if에는 중첩 된 type 유형이 없으므로 SFINAE에서 특수화가 삭제되고 기본 템플릿이 사용됩니다.

당신의 특별한 경우 당신이 정말하지 않습니다, 당신은 하나 하나에 세 가지 요소를 혼합 할 수있는 모든 유형 있었으나 결국 연산자를 전문으로해야 할 것으로 보인다 특정 경우에,

하십시오 이 오래된 질문은 비록

template <typename T> 
class Functor { 
    template <typename U> 
    void op_impl(typename U::Vec* p) const { 
     std::cout << "specialized"; 
    } 
    template <typename U> 
    void op_impl(...) const { 
     std::cout << "general"; 
    } 
public: 
    void operator()() const { 
     op_impl<T>(0); 
    } 
}; 
2

, 나는 여전히 몇 가지를 제공하는 가치가 있다고 생각 : enable_if의 필요성과 특성 클래스를 제거 Vec의 존재 여부에 따라 두 개의 내부 템플릿 기능 중 하나에 전달 Functor 원래 코드를 신속하게 수정하기위한 대안.

기본적으로 문제는 SFINAE (실제로는 괜찮습니다.)가 아니라 기본 템플릿 (void)의 기본 매개 변수와 부분 전문화 (typename T::Vec) 인수가 일치하는 문제입니다. . 기본 템플릿의 기본 매개 변수 때문에 Functor<Foo>은 실제로는 Functor<Foo, void>을 의미합니다. 컴파일러가 특수화를 사용하여 인스턴스화하려고하면 voidstd::vector<int>으로 대체 할 수 없으므로 두 인수를 특성화의 인수와 일치 시키려하고 실패합니다. 그런 다음 기본 템플릿을 사용하여 인스턴스화합니다.

그래서, 모든 Vecstd::vector<int>의있는 가정 빠른 수정,,, 전문화 지금 사용되는이

template<class T, class E = std::vector<int>> 

으로 라인을

template<class T, class V = void> 

을 대체하는 것입니다 때문에 인수가 일치합니다. 단순하지만 너무 제한적입니다. 분명히 우리는 전문 분야의 인수 유형을 기본 템플릿의 기본 매개 변수로 지정할 수있는 것과 일치시키기 위해 더 잘 제어해야합니다. 새 특성을 정의하는 필요로하지 않는 한 빠른 해결책은 이것이다 :

#include <iostream> 
#include <vector> 
#include <type_traits> 

template<class T, class E = std::true_type> 
struct Functor { 
    void operator()() const { 
    std::cerr << "general" << std::endl; 
    } 
}; 

template<class T> 
struct Functor<T, typename std::is_reference<typename T::Vec&>::type> { 
    void operator()() const { 
    std::cerr << "special" << std::endl; 
    } 
}; 

struct Foo { 
    typedef std::vector<int> Vec; 
}; 

int main() { 
    Functor<Foo> ac; 
    ac(); 
} 

이 기본 타입과 배열, 예를 들어, 및 참조하거나 포인터를 포함하여, 여기에 의미를 줄 수있는 Vec 유형에 대해 작동합니다.

1

회원 유형의 존재를 감지하는 또 다른 방법은 void_t을 사용하는 것입니다. 유효한 부분 특수화는 기본 매개 변수와 일치하는 한 일반 구현보다 바람직하기 때문에 유효 할 때 void으로 평가되는 유형이 필요하며 지정된 구성원이있을 때만 유효합니다. 이 유형은 일반적으로 void_t으로 알려진 (C++ 17 기준, 정식 적으로)입니다. 컴파일러가 제대로 지원하지 않는 경우

template<class...> 
using void_t = void; 

이 (초기 C++ 14 컴파일러에서, 별명 템플릿에서 사용되지 않는 매개 변수는 위의 void_t 파괴, SFINAE을 보장하기 위해 보장되지 않은), 해결 방법을 사용할 수 있습니다. C++ 17로

template<typename... Ts> struct make_void { typedef void type; }; 
template<typename... Ts> using void_t = typename make_void<Ts...>::type; 

, void_ttype_traits에서, 유틸리티 라이브러리에서 사용할 수 있습니다.

#include <iostream> 
#include <vector> 
#include <type_traits> // For void_t. 

template<class T, class V = void> 
struct Functor { 
    void operator()() const { 
    std::cerr << "general" << std::endl; 
    } 
}; 

// Use void_t here. 
template<class T> 
struct Functor<T, std::void_t<typename T::Vec>> { 
    void operator()() const { 
    std::cerr << "special" << std::endl; 
    } 
}; 

struct Foo { 
    typedef std::vector<int> Vec; 
}; 

int main() { 
    Functor<Foo> ac; 
    ac(); 
} 

이와 같이 출력은 의도 한대로 special입니다.


이 경우 회원 유형의 존재 여부를 확인하기 때문에 매우 간단합니다. 표현 SFINAE 또는 type_traits 라이브러리없이 수행 할 수 있으므로 필요한 경우 C++ 03 기능을 사용하도록 검사를 다시 작성할 수 있습니다. 내 지식

// void_t: 
// Place above Functor's definition. 
template<typename T> struct void_t { typedef void type; }; 

// ... 

template<class T> 
struct Functor<T, typename void_t<typename T::Vec>::type> { 
    void operator()() const { 
    std::cerr << "special" << std::endl; 
    } 
}; 

, 이것은 대부분의 전부는 아니지만, SFINAE 가능한 C++ 03, C++ 11-, C++ 14- 또는 C++ 1Z 호환 컴파일러 작동합니다 . 이것은 표준보다 조금 뒤처진 컴파일러를 다루거나, C++ 11 호환 컴파일러가 아직없는 플랫폼 용으로 컴파일 할 때 유용 할 수 있습니다.


void_t에 대한 자세한 내용은 cppreference를 참조하십시오.

관련 문제