반응형

함수 찾는 순서

  • 1순위 정확한 매칭(exactly matching) 
  • 2순위 템플릿(template)
  • 3순위 가변 인자(variable argument)
#include <iostream>
using namespace std;

template<typename T>
// 2순위 템플릿(template)
void foo(T t) { cout << "T" << endl; }
// 1순위 정확한 매칭(exactly matching)
void foo(int n) { cout << "int" << endl; }
// 3순위 가변 인자(variable argument)
void foo(...) { cout << "..." << endl; }

int main()
{
    foo(3);
}

 

SFINAE

  • Substitution Failure Is Not An Error

  • 함수 템플릿을 사용시 T의 타입이 결정되고 함수를 생성(Instantiation)하려고 할때 리턴 타입이나 함수 인자등에서 치환에 실패하면 컴파일 에러가 아니라, 함수 후보군에서 제외함

  • 동일한 이름의 다른 함수가 있다면 다른 함수를 사용하게 됨

#include <iostream>
using namespace std;

template<typename T>
typename T::type foo(T t) 
{ 
    cout << "T" << endl; return 0; 
}

void foo(...) 
{ 
    cout << "..." << endl; 
}

int main()
{
    // 가변인자 함수보다 템플릿 함수가 우선순위가 높아서 먼저 참조하게 되지만 리턴 타입에서 
    // int 타입::type은 존재하지 않으므로 만족할 수 없으므로 후보군에서 제외되고 가변인자 버전의 함수가 사용됨
    foo(3); // T
}

 

enable_if

  • c++표준에서 지원하는 도구

  • 1번째 인자가 true일 경우만 type이 정의됨

    • enable_if<true, int>::type  -> int

    • enable_if<true>::type -> void

    • enable_if<false, int>::type -> error

    • enable_if<false>::type -> error

#include <iostream>
using namespace std;

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

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

int main()
{
    //enable_if<true, int>::type t0; // 타입이 int로 결정
    //enable_if<true>::type t1; // 타입이 void로 결정
    //enable_if<false, int>::type t2; // 타입이 없어서 error
}

 

enable_if 예제

  • 정수일때만 동작하는 함수를 만들고 싶을때

    • static_assert

      • 처리하게 되면 조건을 만족하지 않을 경우 컴파일 에러

    • enable_if

      • SFINAE 특성을 활용한 방법

      • 조건을 만족하지 않으면 함수를 생성하지 않고 다른 후보가 있다면 사용함

      • 확장성 유리

#include <iostream>
using namespace std;

// 정수일때만 함수 코드를 생성하고 싶음
template<typename T>
typename enable_if<is_integral<T>::value, int>::type
foo(T a)
{
    cout << "T" << endl;

    return 0;
}

void foo(...)
{
    cout << "not integer" << endl;
}

int main()
{
    foo(2.3);
}

 

enable_if 위치

  • 함수 리턴 타입

  • 함수 인자 타입 -> 생성자에서 주로 사용

  • 템플릿 인자 -> 함수 자체의 모양이 단순해 보이는 장점 있음

#include <iostream>
using namespace std;

// 함수 리턴 타입 위치
template<typename T>
typename enable_if<is_integral<T>::value, int>::type
foo(T a)
{
    cout << "T" << endl;

    return 0;
}

// 함수 인자 타입 위치(리턴 타입이 없는 생성자에서 주로 사용)
template<typename T>
void foo(T a, 
    typename enable_if<
    is_integral<T>::value>::type* = nullptr)
{
    cout << "T" << endl;
}

// 템플릿 인자(함수 자체의 원형이 최대한 유지됨으로 간결해 보이는 장점)
template<typename T, 
    typename enable_if<
    is_integral<T>::value>::type* = nullptr>
void foo(T a)
{
    cout << "T" << endl;
}

// 모두 만족하지 않을때 호출
void foo(...)
{
    cout << "not integer" << endl;
}

int main()
{
    foo(2); // 템플릿 함수 호출(현재 예제코드는 3개의 템플릿이 만족함으로 에러 발생함)
    foo(2.3); // foo(...) 호출
}

 

타입의 종류에 따라 다르게 동작하는 함수 생성 방법

  • type_traits(is_pointer) + if constexpr // C++17에서만 가능
  • type_traits + 함수 오버로딩(false_type, true_type)
    • 디스패치 함수 1개 더 필요
  • type_traits + enable_if
// C++17이상 가능 if constexpr 사용 방법

#include <iostream>
using namespace std;

template<typename T> void printv(const T& v)
{
    if constexpr(is_pointer<T>::value)
        cout << v << ":" << *v << endl;
    else
        cout << v << endl;
}

int main()
{
    int n = 0;
    printv(n);
    printv(&n);
}
// type_traits + 함수 오버로딩(false_type, true_type)

#include <iostream>
using namespace std;

template<typename T>
void printv_imp(const T& v, true_type)
{
    cout << v << ":" << *v << endl;
}

template<typename T>
void printv_imp(const T& v, false_type)
{
    cout << v << ":" << endl;
}

template<typename T> void printv(const T& v)
{
    printv_imp(v, is_pointer<T>());
}

int main()
{
    int n = 0;
    printv(n);
    printv(&n);
}
// type_traits + enable_if 사용 방법

#include <iostream>
using namespace std;

template<typename T> 
typename enable_if<is_pointer<T>::value>::type 
printv(const T& v)
{
    cout << v << ":" << *v << endl;
}

template<typename T> 
typename enable_if<!is_pointer<T>::value>::type
printv(const T& v)
{
    cout << v << ":" << endl;
}

int main()
{
    int n = 0;
    printv(n);
    printv(&n);
}
반응형

+ Recent posts