728x90

Nekoder


Dependent Type


Dependent Type(의존 타입) 이란 뭘까요?

template <typename T>
void func() {
	T::t* p;
}
class A{
	const static int t;
};
class B{
	using t = int;
};

이런 코드가 있다고 해보면, 
A에서는 t 곱하기 p.
B에서는 t 포인터 p를 뜻하게 됩니다.
이처럼 템플릿 인자에 의존하는 타입을 Dependent type(의존 타입)이라고 합니다.
 
그런데 이런 dependent type이 이렇게 보일 때는 쉬워 보일 수도 있지만 다음 코드를 보면 좀 더 구체적으로 체감이 됩니다.

template <int N>
struct INT {
	static const int num = N;
};
template <typename a, typename b>
struct add{
	typedef INT<a::num + b::num> result;
};
template <typename a, typename b>
struct divide {
	typedef INT<a::num / b::num> result;
};
using one = INT<1>;
using two = INT<2>;
using three = INT<3>;

template <typename N, typename d>
struct check_div {
	static const bool result = 
    	(N::num % d::num == 0) || check_div<N,add<d, one>::result>::result;
};
template <typename N>
struct is_prime {
	static const bool result = !check_div<N, two>::result;
};
template <>
struct is_prime<two>{
	static const bool result = true;
};
template <>
struct is_prime<three> {
	static const bool result = true;
};
template <typename N>
struct check_div<N, divide<N, two>::result{
	static const bool result = (N::num % (N::num /2)==0);
};

 이 코드는 모두의 코드에서 가져온 부분입니다.

 
이 코드는 template meta programming을 이용해서, 소수 판별 프로그램을 만드는 코드인데, 문제가 있습니다.
INT<1>, INT<2> 즉, one, two는 서로 다른 타입입니다.
template <int N>을 기반으로 하는 INT라는 구조체를 기반으로 하지만 N이 달라지면 다른 타입의 구조체가 생성되는 것입니다.
이렇게 같은 템플릿을 기반으로 하지만 인자가 다르기 때문에 전혀 다른 타입입니다. 
 
그래서 check_div에 보면 내부에 add<d, one>::result가 있습니다.
이 부분이 타입인지 확신 할 수 없기 때문에 컴파일이 진행되지 않습니다.
그래서

template<typename N, typename d>
struct check_div{
	static const bool result = 
    	(N::num % d::num == 0) || check_div<N, typename add<d, one>::result::result;
};

이렇게 앞에다가 typename을 붙여 타입이라고 명시하면 해당 부분이 컴파일됩니다.
 

추가 : 고민(나는 아직 N이나 d에 구체적인 값을 안 넣었는데, typename만 붙였다고 왜 컴파일이 될까?)

더보기

컴파일 과정에서 템플릿 인스턴스 전에 파싱이 동작하는데, 이때 템플릿의 구조를 해석한다.

 

컴파일러는 이 과정에서 이러한 표현(typename이 없는)을 보면 해당 표현이 타입인지 확신할 수 없다.

그렇게 되면 파싱 자체를 할 수 없기 때문에 컴파일 에러가 발생한다.

파싱을 먼저 해야 템플릿 인스턴스가 가능하기 때문에, typename을 붙여 파싱과정에서 타입이라고 인식하고 넘어가게 한다.

그래서 그 후에 인스턴스 시점에 템플릿 인자들이 들어오면, 타입이 확정되고 연산이 컴파일 타임에 수행된다.


 

Auto


 
Auto는 템플릿의 사용으로 복잡해진 타입의 이름들을 간단하게 나타낼 수 있는 획기적 방법입니다.

#include <iostream>
#include <string>

int sum(int a, int b){
	return a+b;
}
class SomeClass{
  private:
    int data;
  public:
    SomeClass(int d) : data(d) {}
    SomeClass(const SomeClass& s) : data(s.data) {}
};
int main() {
	auto c = sum(1, 2);
    auto num = 1.5 + 3.5;
    SomeClass some(10);
    auto some2 = some;
    auto some3(10);
    
    std::cout << "c [type] : " << typeid(c).name() << std::endl;
    std::cout << "num [type] : " << typeid(num).name() << std::endl;
    std::cout << "some2 [type] : " << typeid(some2).name() << std::endl;
    std::cout << "some3 [type] : " << typeid(some3).name() << std::endl;
    return 0;
}

위와 같은 코드가 있다고 합시다.
 
auto로 타입을 지정하고 타입을 출력하면,

c [type] : i
num [type] : d
some2 [type] : 9SomeClass
some3 [type] : i

이렇게 출력이 될 겁니다. some3는 class생성하려고 한 거 아니야?라고 생각하셨다면 그 이유에 대해서 말씀드릴게요.
 
some3(10);은 함수의 형태를 하고 있습니다. 그래서 auto some3(10);을 하면 
컴파일러는 이를 "some3는 int를 인자로 받고 반환형이 auto인 함수의 선언이구나. "라고 해석합니다.
그래서 그 순간부터 some3는 함수 이름으로 취급되지만, 해당 함수는 메모리에 존재하지 않기 때문에,
typeid(some3)는 그저 typeid(10)으로 동작하고 int 리터럴인 10을 받아 타입을 출력해 i가 나오는 것입니다.
 
이처럼 함수 선언처럼 해석될 수 있다면, 선언으로 해석해야 한다는 C++ 규칙에서 비롯된 부작용
이를, Most Vexing Parse라고 부릅니다.
 


Template Argument Deduction


 
여기서 auto가 타입을 추측하는 방법과, template에서 타입을 추측하는 방법이 같습니다. 이를 좀 살펴보겠습니다.
 

template argument deduction?

In C++, when using templates, template argument deduction refers to the compiler's ability to automatically determine the template parameters types based on the givben arguments.

C++에서 템플릿을 사용할 때, 템플릿의 인자를 생략하면 컴파일러가 표현 식으로부터 어떤 타입을 사용할지 자동으로 추론하는 것을 말한다.
 

How Deduction Works

Template arguments are deduced from the arguments passed during a function call.
When a function template is called, the compiler compares the passed arguments with the parameters of the template, thus deciding the types for the template parameters.

템플렛 인자는 함수 호출에서 전달된 인수로부터 추론된다.
함수 템플릿이 호출될 때, 컴파일러는 전달된 인수와 함수 템플릿의 매개변수를 비교하여, 템플릿 매개변수의 타입을 결정한다.

 

Deduction process

1. The exact match rule takes highest priority.
2. The compiler evaluates potential template argument candidates until parameters and arguments exactly match.
3. If there are multiple or no matching candidates, deduction fails, resulting in a compilation error.

1. 정확한 매칭 규칙이 가장 우선된다.
2. 매개변수와 인수가 정확히 대응될 때까지 컴파일러는 템플릿 인자 후보를 평가한다.
3. 만약 여러 후보가 있거나, 후보가 없으면, 추론에 실패하며 컴파일 에러가 발생한다.
 

 

728x90