C ++에서 SFINAE 기능에 접근


40

프로젝트에서 SFINAE 기능을 많이 사용하고 있으며 다음 두 가지 접근 방식 (스타일 이외)에 차이가 있는지 확실하지 않습니다.

#include <cstdlib>
#include <type_traits>
#include <iostream>

template <class T, class = std::enable_if_t<std::is_same_v<T, int>>>
void foo()
{
    std::cout << "method 1" << std::endl;
}

template <class T, std::enable_if_t<std::is_same_v<T, double>>* = 0>
void foo()
{
    std::cout << "method 2" << std::endl;
}

int main()
{
    foo<int>();
    foo<double>();

    std::cout << "Done...";
    std::getchar();

    return EXIT_SUCCESS;
}

프로그램 출력은 다음과 같습니다.

method 1
method 2
Done...

방법 2가 스택 오버 플로우에서 더 자주 사용되는 것을 보았지만 방법 1을 선호합니다.

이 두 가지 접근 방식이 다른 상황이 있습니까?


이 프로그램을 어떻게 운영합니까? 나를 위해 컴파일되지 않습니다.
igel dec

@alter igel에는 C ++ 17 컴파일러가 필요합니다. 이 예제를 테스트하기 위해 MSVC 2019를 사용했지만 주로 Clang과 함께 작업합니다.
keith

관련 : 왜-피해야 할 함수형 서명 과 C ++ 20은 개념과 함께 새로운 방법을 도입합니다 :-)
Jarod42

@ Jarod42 Concepts는 C ++ 20에서 가장 필요한 것 중 하나입니다.
val은 Reinstate Monica가

답변:


35

방법 2가 스택 오버 플로우에서 더 자주 사용되는 것을 보았지만 방법 1을 선호합니다.

제안 : 방법 2를 선호하십시오.

두 방법 모두 단일 기능으로 작동합니다. 동일한 서명을 가진 함수를 두 개 이상 가지고 있고 세트의 하나의 기능 만 사용하려는 경우 문제가 발생합니다.

활성화한다고 가정 foo()버전 1, bar<T>()(그것이 척 constexpr기능)입니다 true, 그리고 foo()경우, 버전 2 bar<T>()입니다 false.

template <typename T, typename = std::enable_if_t<true == bar<T>()>>
void foo () // version 1
 { }

template <typename T, typename = std::enable_if_t<false == bar<T>()>>
void foo () // version 2
 { }

모호성이 있기 때문에 컴파일 오류가 발생합니다. foo()서명이 동일한 두 함수 (기본 템플릿 매개 변수는 서명을 변경하지 않습니다).

그러나 다음 해결책

template <typename T, std::enable_if_t<true == bar<T>(), bool> = true>
void foo () // version 1
 { }

template <typename T, std::enable_if_t<false == bar<T>(), bool> = true>
void foo () // version 2
 { }

SFINAE가 함수의 서명을 수정하기 때문에 작동합니다.

관련없는 관찰 : 세 번째 방법이 있습니다 : 반환 유형 활성화 / 비활성화 (클래스 / 구조체 생성자를 제외하고는 분명히)

template <typename T>
std::enable_if_t<true == bar<T>()> foo () // version 1
 { }

template <typename T>
std::enable_if_t<false == bar<T>()> foo () // version 2
 { }

방법 2로서, 방법 3은 동일한 서명을 갖는 대안적인 기능의 선택과 호환된다.


1
좋은 설명을 주셔서 감사합니다, 지금부터 방법 2 & 3을 선호합니다 :-)
keith

"기본 템플릿 매개 변수는 서명을 변경하지 않습니다" -기본 템플릿 매개 변수를 사용하는 두 번째 변형에서는 어떻게 다른가요?
Eric

1
@Eric-간단하지 않습니다 ... 다른 답변이 이것을 더 잘 설명한다고 가정합니다 ... SFINAE가 기본 템플릿 인수를 활성화 / 비활성화 foo()하면 명시 적 두 번째 템플릿 매개 변수 ( foo<double, double>();호출)로 호출 할 때 함수를 사용할 수 있습니다 . 사용 가능한 상태로 유지되면 다른 버전과 모호합니다. 방법 2에서 SFINAE는 기본 매개 변수가 아닌 두 번째 인수를 활성화 / 비활성화합니다. 따라서 두 번째 매개 변수를 허용하지 않는 대체 실패가 있으므로 매개 변수를 설명하는 호출 할 수 없습니다. 따라서 버전을 사용할 수 없으므로 모호함이 없습니다
max66

3
방법 3은 일반적으로 symbol-name으로 누출되지 않는 추가 이점이 있습니다. 변형 auto foo() -> std::enable_if_t<...>은 종종 함수 서명을 숨기지 않고 함수 인수를 사용할 수 있도록하는 데 유용합니다.
중복 제거기

@ max66 : 요점은 매개 변수가 제공되고 기본값이 필요하지 않은 경우 템플릿 매개 변수 기본값의 대체 실패가 오류가 아니라는 것입니다.
Eric

21

max66의 답변 외에도 방법 2를 선호하는 또 다른 이유는 방법 1을 사용하면 명시 적으로 유형 매개 변수를 두 번째 템플릿 인수로 전달하고 SFINAE 메커니즘을 완전히 무시할 수 있기 때문입니다. 오타, 복사 / 붙여 넣기 오류 또는 더 큰 템플릿 메커니즘에 대한 감독으로 발생할 수 있습니다.

#include <cstdlib>
#include <type_traits>
#include <iostream>

// NOTE: foo should only accept T=int
template <class T, class = std::enable_if_t<std::is_same_v<T, int>>>
void foo(){
    std::cout << "method 1" << std::endl;
}

int main(){

    // works fine
    foo<int>();

    // ERROR: subsitution failure, as expected
    // foo<double>();

    // Oops! also works, even though T != int :(
    foo<double, double>();

    return 0;
}

여기에 라이브 데모


좋은 지적. 메커니즘을 가로 챌 수 있습니다.
max66
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.