자료형 추론 (auto, decltype)
C++11
자료형을 기본 타입 형태로 추론한다.
자료형을 명시하기 복잡하거나 귀찮을 때 사용한다.
가독성이 좋다.
함수의 반환 값에도 적용할 수 있다.
Trailing Return Type(후행 반환 형식)
auto
키워드로 정의된 함수 반환 값의 자료형을 명시하는 것함수 앞에 자료형을 쓰는 것보다 가독성이 좋고, 인덴팅을 맞추기도 편하다.
auto add(int x, int y) -> int; auto add(double x, double y) -> double;
예제
iterator
대체
개인적으로 이 때 제일 많이 사용하는 것 같다.
#include <iostream> #include <vector> int main() { using namespace std; vector<int> vec({ 1, 2, 3 }); for (vector<int>::iterator itr = vec.begin(); itr != vec.end(); ++itr) cout << *itr; cout << endl; for (auto itr = vec.begin(); itr != vec.end(); ++itr) cout << *itr; cout << endl; for (const auto & itr : vec) cout << itr; cout << endl; } /* stdout 123 123 123 */
기본 자료형 대입
작동 원리는 템플릿 타입 추론처럼 컴파일할 때 값을 복사해서 인스턴스화한다고 생각하면 될 것 같다.
int main() { using namespace std; int x = int(); auto auto_x = x; // int const int& crx = x; auto auto_crx1 = crx; // int const auto& auto_crx2 = crx; // const int & volatile int vx = 1024; auto avx = vx; // int volatile auto vavx = vx; // volatile int }
auto_crx1
의 경우 원본인crx
가const int&
자료형이므로 그 값을R-value
로 받아서 복사하는 것이다.auto_crx2
는crx
가const int&
자료형이므로 값이int
형이고, 따라서const int&
와 같아진다.avx
의 경우에도vx
를R-value
처럼 값만 복사하여int
자료형이 되는 것이다.
템플릿 타입 추론 (Template Type Deduction)
auto
키워드와 비슷하게 자료형을 추론해낸다.template <typename T> void func(T arg) {} int main() { const int& crx = 123; func(crx); // void func<int>(int arg) }
template <typename T> void func(const T& arg) {} int main() { const int& crx = 123; func(crx); // void func<int>(const int& arg) }
레퍼런스
auto &
의 형태로 사용하면L-value
레퍼런스를 참조하는 것이기 때문에const
등의 속성이 유지된다.int main() { volatile int c = 0; auto& rc = c; // const int & }
L-value, R-value References
auto &&
의 형태로 사용하면 상황에 따라 달라진다.int main() { int i = 42; auto&& ri_1 = i; // int & auto&& ri_2 = 42; // int && }
const 포인터
포인터의
const *
속성은 유지된다.- 원본은 건드리면 안된다는 속성을 유지해야 하기 때문이다.
int main() { int x = 42; const int* p1 = &x; auto p2 = p1; // const int* }
자료형이 정해지지 않은 변수 사용 예제
템플릿에 어떤 자료형이 들어오는가에 따라 변수의 자료형이 달라지는데,
auto
로 간단하게 처리할 수 있다.template <typename T, typename S> void func(T lhs, S rhs) { auto prod1 = lhs * rhs; typedef decltype(lhs* rhs) product_type; product_type prod2 = lhs * rhs; decltype(lhs * rhs) prod3 = lhs * rhs; }
product_type
은auto
키워드가 등장하기 이전에 사용했던 방식이다.decltype
- 일부 컴파일러에서 관습적으로 사용하던
typeof
를 공식적으로 내놓은 것이다.
- 일부 컴파일러에서 관습적으로 사용하던
자료형이 정해지지 않은 반환 값 정의 예제
auto
와decltype
을 같이 사용하여 쉽게 해결할 수 있다.template <typename T, typename S> auto func(T lhs, S rhs) -> decltype(lhs* rhs) { return lhs * rhs; }
auto와 decltype의 차이
decltype
은 변수가 선언될 때의 자료형을 가져온다.decltype
내부에 괄호가 감싸져있으면L-value
레퍼런스로 가져온다.
class S { public: int x_; S() { x_ = 42; } }; int main() { int x; const int cx = 42; const int& crx = x; const S* p = new S(); auto a = x; // int auto b = cx; // int auto c = crx; // int auto d = p; // const S* auto e = p->x_; // int typedef decltype(x) x_type; // int typedef decltype(cx) cx_type; // const int typedef decltype(crx) crx_type; // const int& typedef decltype(p->x_) x__type; // int typedef decltype((x)) x_with_parens_type; // int & typedef decltype((cx)) cx_with_parens_type; // const int& typedef decltype((crx)) crx_with_parens_type; // const int& typedef decltype((p->x_)) x__with_parens_type; // const int& }
std::vector
의 경우[]
연산자 오버로딩이L-value
레퍼런스를 반환하기 때문에decltype
이 레퍼런스로 잡힌다.#include <iostream> #include <vector> class S { public: int x_; S() { x_ = 42; } }; const S foo() { return S(); } const int& foobar() { return 123; } int main() { using namespace std; vector<int> vec = { 42, 43 }; auto a = foo(); // S typedef decltype(foo()) foo_type; // const S auto b = foobar(); // int typedef decltype(foobar()) foobar_type; // const int& auto itr = vec.begin(); // vector<int>::iterator typedef decltype(vec.begin()) iterator_type; // vector<int>::iterator auto first_element = vec[0]; // int decltype(vec[1]) second_element = vec[1]; // int& }
decltype
에서 삼항연산자 등으로 값을 그대로 반환하게 될 경우L-value
레퍼런스로 추론된다.int main() { int x = 0; int y = 0; const int cx = 42; const int cy = 43; double d1 = 3.14; double d2 = 2.72; typedef decltype(x * y) prod_xy_type; // int auto a = x * y; // int typedef decltype(cx * cy) prod_xy_type; // int auto a = cx * cy; // int typedef decltype(d1 < d2 ? d1 : d2) cond_type; // double& auto c = d1 < d2 ? d1 : d2; // double typedef decltype(x < d2 ? x : d2) cond_type_mixed; // double auto d = x < d2 ? x : d2; // double }
위의
L-value
레퍼런스로 반환하는 특성때문에,std::remove_reference
구조체를 이용해야 할 때도 있다.- 반환 값이
L-value
레퍼런스일 때도 있고R-value
일 때도 있으면 헷갈리기 때문이다.
#include <iostream> template <typename T, typename S> auto fpmin_wrong(T x, S y) -> decltype(x < y ? x : y) { return x < y ? x : y; } template <typename T, typename S> auto fpmin(T x, S y) -> typename std::remove_reference<decltype(x < y ? x : y)>::type { return x < y ? x : y; } int main() { int i = 42; double d = 45.1; //auto a = std::min(i, d); // 에러 auto a = std::min(static_cast<double>(i), d); int& j = i; typedef decltype(fpmin_wrong(d, d)) fpmin_wrong_return_type1; // double& typedef decltype(fpmin_wrong(i, d)) fpmin_wrong_return_type2; // double typedef decltype(fpmin_wrong(j, d)) fpmin_wrong_return_type3; // double typedef decltype(fpmin(d, d)) fpmin_return_type1; // double typedef decltype(fpmin(i, d)) fpmin_return_type2; // double typedef decltype(fpmin(j, d)) fpmin_return_type3; // double }
- 반환 값이
decltype
은 실제로 실행시키지 않고 추론만 하므로, 유효하지 않음에도 사용할 수 있는 경우가 있다.#include <vector> int main() { std::vector<int> vec; typedef decltype(vec[0]) integer; // int& }
decltype
은nested type
에 접근하기에도 유용하다.template <typename R> class SomeFunctor { public: typedef R result_type; }; int main() { SomeFunctor<int> func; typedef decltype(func)::result_type integer; // int }
lambda
함수에도 적용이 가능하다
람다 함수 역시
decltype
에서 소괄호로 한번 더 감싸주면L-value
레퍼런스로 추론된다.#include <iostream> int main() { using namespace std; auto lambda = []() { return 42; }; decltype(lambda) lambda2(lambda); decltype((lambda)) lambda3(lambda); cout << &lambda << '\n' << &lambda2 << '\n' << &lambda3 << endl; } /* stdout 0053F717 0053F70B 0053F717 */
람다 함수의 파라미터에는
auto
키워드가 올 수 있다.#include <iostream> int main() { using namespace std; auto lambda = [](auto x, auto y) { return x + y; }; cout << lambda(1.1, 2) << endl; cout << lambda(3, 4) << endl; cout << lambda(4.5, 2.2) << endl; } /* stdout 3.1 7 6.7 */
'C++ > Syntax' 카테고리의 다른 글
C++ 열거형 (Enumerate Type) (0) | 2021.03.10 |
---|---|
C++ 형변환 (Type Conversion) (0) | 2021.03.10 |
C++ using (0) | 2021.03.10 |
C++ extern (0) | 2021.03.10 |
C++ 범위 지정 연산자 (Scope Resolution Operator) (0) | 2021.03.10 |