과제에서, 범위지정자앞에, 즉 네임스페이스는 ft라고 명시함.
#include <iostream>
namespace ft
{
int i = 5;
}
int i = 4;
int main(void)
{
int i = 3;
std::cout << i << std::endl;
std::cout << ::i << std::endl;
std::cout << ft::i << std::endl;
return (0);
}
3
4
5
우선적으로 왜 enable_if를 사용하는지 알아보자!
template<typename Iter>
void test(Iter a, Iter b)
{
// ...
}
이런 함수가 있다고 가정할때, 우리는 인자로서, iterator를 넣고싶기때문에, 함수 파라미터의 타입을 Iter로 정하였다.
하지만, 컴퓨터는 Iter를 int값으로 받아도, std::string으로 받아도, iterator를 받아도 구별하지못한다.
즉, enable_if를 이용하여, 받은 인자의 값이 정말로, iterator인지를 확인할수있다.
template <class InputIterator>
vector(
InputIterator first,
InputIterator last,
const allocator_type& alloc = allocator_type(),
typename ft::enable_if<!ft::is_integral<InputIterator>::value, InputIterator>::type* = 0)
: m_size(0), m_capacity(0), m_alloc(alloc)
{
this->assign(first, last);
}
다음은 벡터의 생성자중의 하나이다.
prototype은 다음과 같다.
template <class InputIterator>
vector (InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
여기서 우리는 우선적으로, 제일 마지막에 위치한 파라미터만을 보기로한다.
typename ft::enable_if<!ft::is_integral<InputIterator>::value, InputIterator>::type* = 0
이것을 뜯어보자면, 우선은 enable_if 안에 위치한 is_integral을 볼수있다.
아래는, is_integral 구현부분이다.
// intergral type에 해당하는 자료형이 아닌경우 (특수화한 타입의 인자)
// false 출력
// true 일경우, SFINAE에 따라, 오버로딩후 type 생성
template <typename T>
struct is_integral {
static const bool value = false;
};
// integral type에 해당하는 자료형 타입을 위한 템플릿 특수화
template <> struct is_integral<bool> { static const bool value = true; };
template <> struct is_integral<char> { static const bool value = true; };
template <> struct is_integral<wchar_t> { static const bool value = true; };
template <> struct is_integral<signed char> { static const bool value = true; };
template <> struct is_integral<short int> { static const bool value = true; };
template <> struct is_integral<int> { static const bool value = true; };
template <> struct is_integral<long int> { static const bool value = true; };
template <> struct is_integral<long long int> { static const bool value = true; };
template <> struct is_integral<unsigned char> { static const bool value = true; };
template <> struct is_integral<unsigned short int> { static const bool value = true; };
template <> struct is_integral<unsigned int> { static const bool value = true; };
template <> struct is_integral<unsigned long int> { static const bool value = true; };
template <> struct is_integral<unsigned long long int> { static const bool value = true; };
위와같이, 타입변수가 false일경우에는 iterator로 볼수있다.
따라서,
!ft::is_integral<InputIterator>::value
이 부분은, iterator가 타입변수일때, false를 반환하고,
!와 연산되어, true가 된다.
다음은 enable_if이다.
// B가 true일때, type을 생성.
// 아닐경우, 컴파일 하지않고, 오버로딩 대상을 찾음.
// typename T = void
// client code를 위한 더미 파라미터
template <bool cond, typename T = void>
struct enable_if {
};
template <typename T>
struct enable_if<true, T> {
typedef T type;
};
enable_if의 첫번째 타입변수값이 true일때, type을 생성.
typename ft::enable_if<true, InputIterator>::type* = 0
타입을 생성한다는것은, 컴파일이 된다는것이고, 그렇다는것은 vector함수가 실행된다는것이다.
타입이 생성되지않는다면, 다른 후보군을 찾는다.
참고)
::type* = 0
부분에서, 0을 주는이유는, client code를 위해서이다.
int test(int a, float b = 0)
{
std::cout << a * 2 << std::endl;
return (a * 2);
}
int main(void)
{
test(2); // for client code == test(2, 0);
test(4, 4.2f); // possible
return (0);
}