자료형 추론 (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 */
'SW개발 > C++' 카테고리의 다른 글
| C++ 형변환 (Type Conversion) (0) | 2021.03.10 |
|---|---|
| C++ typeinfo (0) | 2021.03.10 |
| C++ using (0) | 2021.03.10 |
| C++ extern (0) | 2021.03.10 |
| 따라하며 배우는 C++ 4장 (0) | 2021.03.10 |