* decltype 숙지
decltype은 주어진 이름이나 표현식의 형식을 알려준다. 대부분의 경우는 decltype이 독자가 예측한 바로 그 형식을 말해주지만, 아주 가끔은 예상 밖의 결과를 제공하기 때문에 머리를 긁적이면서 온라인 Q&A 사이트들을 뒤져봐야 하는 사태가 벌어지기도 한다. 대부분의 경우, 뜻밖의 결과로 곤란하지 않은 케이스부터 살펴보도록 한다. 템플릿과 auto의 형식 연역(1), (2)에서 다룬 내용에서 일어났던 일과는 달리, decltype은 주어진 이름이나 표현식의 구체적인 형식을 그대로 말해주는 특징이 있다.
<의사 코드>
const int i = 0; // decltype(i)는 const int
bool f(const Widget& w); // decltype(w)는 const Widge&, decltype(f)는 bool(const Widget&)
struct Point
{
int x, y;
} // decltype(Point::x)는 int, decltype(Point::y)는 int
Widget w; // decltype(w)는 Widget
if (f(w)) ... // decltype(f(w))는 bool
template<typename T>
class vector
{
public:
...
T& operator[] (std::size_t index);
...
}; // std::vector를 단순화한 버전
vector<int> v; // decltype(v)는 vector<int>
...
if (v[0] == 0) ... // decltype(v[0]])은 int&
위 예들에 대해서는 예외사항이 없다.
C++11에서 decltype은 함수의 반환 형식이 그 매개변수 형식들에 의존하는 함수 템플릿을 선선언할 때 주로 쓰인다. 예를 들어 컨테이너 하나와 색인 하나를 받고, 우선 사용자를 인증한 후 대괄호 색인화를 통해서 (즉, 컨테이너[색인] 구문을 이용해서) 컨테이너의 한 요소를 돌려주는 함수를 작성한다고 하자. 그러한 함수의 환 형식은 반드시 색인화 연산의 반환 형식과 동일해야 한다.
대체로, 형식 T의 객체들을 담은 컨테이너에 대한 operator[] 연산은 T&를 돌려준다. 예를 들어 std::deque가 그렇고, std::vector도 거의 항상 그렇다. 그러나 std::vector<bool>에 대한 operator[]는 bool&가 아니라 완전히 새로운 객체를 돌려준다. 왜 그런지, 그리고 어떤 일이 일어나는지는 (6)Chapter에서 계속 설명한다. 여기서 중요한 저점은, 컨테이너의 operator[]의 반환 혀형식이 컨테이너에 따라 다를 수 있다는 점이다. decltype을 이용하면 그런 함수의 반환 형식을 손쉽게 표현할 수 있다. 다음은 우리가 작성하고자 하는 템플릿 함수의 첫 버전으로, decltype을 이용해서 반환 형식을 계산하는 방법을 보여준다. 이 템플릿에는 다듬어야 할 부분이 남아있지만, 그러한 정련(refinement)은 나중으로 미루도록 한다.
<의사 코드>
template<typename Container, typename Index> // 작동하지만, 좀 더 정련할 필요가 있다
auto authAndAccess(Container& c, Index i) -> decltype(c[i])
{
authenticateUser();
}
함수 이름 앞에 auto를 지정하는 것은 형식 연역과는 아무런 관련이 없다. 그 auto는 여기에 C++11의 후행 반환 형식(trailing return type) 구문이 쓰인다는 점을, 다시 말해서 함수의 반환 형식을 이 위치가 아니라 매개변수 목록 다음에 ("->" 다음 위치에) 선언하겠다는 점을 나타내는 것일 뿐이다. 이러한 후행 반환 형식 구문에는 반환 형식을 매개변변수들을 이용해서 지정할 수 있다는 장점이 있다. 예를 들어 authAndAccess에서는 매개변수 c와 i를 이용해서 반환 형식을 지정했다. 만일 통상적인 방식으로 함수 이름 앞에서 반환 형식을 지정정한다면, c와 i는 사용할 수 었다(둘 다 아직 선언되지 않았으므로). 이런 식으로 선언된 authAndAccess의 반환 형식은 인수로 주어진 컨테이너에 적용된 operator[]의 반환 형식과 동일하다. 이는 우리가 애초에 원했던 것과 일치하는 결과이다.
C++은 람다 함수가 한 문장으로 이루어져 있다면 그 반환 형식의 연역을 허용하며, C++14는 허용 범위를 더욱 확장해서 모든 람다와 모든 함수의 반환 형식 연역을 허용한다(심지어 return 문이 여러 개인 함수도 - 단, 모든 return 문의 형식 연역 결과가 일치해야 한다). 따라서, authAndAccess의 경우 C++14에서는 후행 환 형식을 생략하고 그냥 함수 이름 앞의 auto만 남겨 두어도 된다. 그런 형태의 선언에서는 실제로 auto가 형식 연역이 일어남을 뜻하는 용도로 쓰인다. 좀 더 추체적으로, 그러한 auto는 컴파일러가 함수의의 구현으로부터 함수의 반환 형식을 연역할 것임을 뜻한다.
*** Key Point ***
- decltype은 항상 변수나 표현식의 형식을 아무 수정 없이 보고한다.
- decltype은 형식이 T이고 이름이 아닌 왼값 표현식에 대해서는 항상 T& 형식을 보고한다.
- C++14는 decltype(auto)를 지원한다. decltype(auto)는 auto처럼 초기치로부터 형식을 연역하지만, 그 형식 연역 과정에서 decltype의 규칙들을 적용한다.
'컴퓨터과학' 카테고리의 다른 글
[Effective C++] (5-1) 명시적 형식 선언보다 auto 선호하기 (0) | 2020.05.23 |
---|---|
[Effective C++] (4-2) 연역된 형식을 파악하는 방법 (0) | 2020.05.23 |
[Effective C++] (4-1) 연역된 형식을 파악하는 방법 (0) | 2020.05.23 |
[Effective C++] (2) auto의 형식 연역 규칙 숙지하기 (0) | 2020.05.22 |
[Effective C++] (1) 템플릿 형식 연역 규칙 숙지하기 (0) | 2020.05.22 |