대체 실패는 오류가 아닙니다.

Substitution failure is not an error

치환 실패는 오류가 아닙니다(SFINAE). 템플릿파라미터의 비활성 치환 자체가 오류가 아닌 C++의 상황을 말합니다.David Vandevoorde는 관련 프로그래밍 [1]기술을 설명하기 위해 SFINAE라는 약어를 처음 도입했습니다.

특히 과부하 해결 후보 세트를 작성할 때 해당 세트의 일부(또는 모든) 후보는 대응하는 템플릿파라미터 대신 (잠재적으로 추론될 가능성이 있는) 템플릿 인수를 사용하여 인스턴스화된 템플릿의 결과일 수 있습니다.특정 템플릿에 대한 일련의 인수 치환 중에 오류가 발생하면 C++ 표준이 그러한 [2]처리를 허가하는 경우, 컴파일러는 컴파일 오류로 정지하는 대신 후보 집합에서 잠재적인 과부하를 제거합니다.1개 이상의 후보가 남아 과부하 해결에 성공하면 호출이 적절하게 이루어집니다.

다음으로 SFINAE의 기본 인스턴스를 나타냅니다.

구조 시험 {   유형화된 인트 후우; };  템플릿 <>타이프네임 T> 무효 f(타이프네임 T::후우) {}  // 정의 #1  템플릿 <>타이프네임 T> 무효 f(T) {}  // 정의 #2  인트 주된() {   f<>시험>(10);  // 1번 콜   f<>인트>(10);   // 2번 콜오류 없음(int::foo가 없는 경우에도)                 // SFINAE 덕분입니다. } 

여기서 정규화된 이름에 클래스 이외의 유형을 사용하려고 합니다(T::foo)는 다음 항목에 대한 차감 실패의 원인이 됩니다.f<int>왜냐면int이름이 붙은 중첩 유형이 없습니다.foo그러나 유효한 함수가 후보 함수 집합에 남아 있기 때문에 프로그램은 잘 형성되어 있습니다.

SFINAE는 관련 없는 템플릿 선언이 표시될 때(예를 들어 헤더 파일 포함) 잘못된 형식의 프로그램을 만드는 것을 피하기 위해 처음 도입되었지만, 많은 개발자들은 나중에 컴파일 시간 인스펙션에 이 동작이 유용하다는 것을 알게 되었다.구체적으로는 템플릿이 인스턴스화 시 템플릿 인수의 특정 속성을 결정할 수 있습니다.

예를 들어 SFINAE를 사용하여 유형에 특정 typedef가 포함되어 있는지 여부를 확인할 수 있습니다.

#실패하다 <iostream>  템플릿 <>타이프네임 T> 구조 has_sysef_foobar {   // 유형 "yes"와 "no"는 서로 다른 크기를 가집니다.   // size of(yes) == 1 및 size of(no) == 2입니다.   유형화된  네.[1];   유형화된  아니요.[2];    템플릿 <>타이프네임 C>   정적인 네.& 시험(타이프네임 C::푸바*);    템플릿 <>타이프네임>   정적인 아니요.& 시험(...);    // 호출 테스트 결과의 "sizeof"(nullptr)가 다음과 같은 경우   // size of(yes), 첫 번째 오버로드가 작동하며 T에는 다음과 같은 이름의 중첩 유형이 있습니다.   // foobar.   정적인 컨스턴트 부울 가치 = 크기(시험<>T>(특수)) == 크기(네.); };  구조 후우 {   유형화된 흘러가다 푸바; };  인트 주된() {   표준::외치다 << > 표준::부루알파;   표준::외치다 << > has_sysef_foobar<>인트>::가치 << > 표준::;  // false 출력   표준::외치다 << > has_sysef_foobar<>후우>::가치 << > 표준::;  // true 출력 } 

언제T네스트된 유형이 있습니다.foobar정의, 첫 번째의 인스턴스화test동작하고 늘 포인터 상수가 정상적으로 전달됩니다.(그리고 그 결과의 표현 유형은 다음과 같습니다).yes동작하지 않는 경우는, 2번째의 기능 밖에 사용할 수 없습니다.test그 결과 나타나는 식의 유형은 다음과 같습니다.no생략 부호는 임의의 인수를 받아들일 뿐만 아니라 변환 순위가 가장 낮기 때문에 사용할 수 있으면 첫 번째 함수에 대한 호출이 우선되므로 모호함이 해소됩니다.

C++11 심플화

C++11에서는 위의 코드를 다음과 같이 간략화할 수 있습니다.

#실패하다 <iostream> #실패하다 <type_timeouts>  템플릿 <>타이프네임... Ts> 사용. 무효 = 무효;  템플릿 <>타이프네임 T, 타이프네임 = 무효> 구조 has_sysef_foobar : 표준::false_type {};  템플릿 <>타이프네임 T> 구조 has_sysef_foobar<>T, 무효<>타이프네임 T::푸바>> : 표준::true_type {};  구조 후우 {   사용. 푸바 = 흘러가다; };  인트 주된() {   표준::외치다 << > 표준::부루알파;   표준::외치다 << > has_sysef_foobar<>인트>::가치 << > 표준::;   표준::외치다 << > has_sysef_foobar<>후우>::가치 << > 표준::; } 

라이브러리 기본 v2(n4562) 제안에서 검출 관용어를 표준화하면 위의 코드를 다음과 같이 다시 작성할 수 있습니다.

#실패하다 <iostream> #실패하다 <type_timeouts>  템플릿 <>타이프네임 T> 사용. has_sysef_foobar_t = 타이프네임 T::푸바;  구조 후우 {   사용. 푸바 = 흘러가다; };  인트 주된() {   표준::외치다 << > 표준::부루알파;   표준::외치다 << > 표준::is_filename(이것들)<>has_sysef_foobar_t, 인트>::가치 << > 표준::;   표준::외치다 << > 표준::is_filename(이것들)<>has_sysef_foobar_t, 후우>::가치 << > 표준::; } 

Boost 개발자는 SFINAE를 boost::enable_if[3] 등의 방법으로 사용했습니다.

레퍼런스

  1. ^ Vandevoorde, David; Nicolai M. Josuttis (2002). C++ Templates: The Complete Guide. Addison-Wesley Professional. ISBN 0-201-73484-2.
  2. ^ 국제 표준화 기구"ISO/IEC 14882:2003, 프로그래밍 언어 - C++", © 14.8.2.
  3. ^ Boost Enable If(부스트 유효화)