C++ 중급 - Type Deduction 2

타입·2024년 2월 20일
0

C++ 공부

목록 보기
11/17

Decltype Type Deduction

  • decltype(expression)
    () 안에 있는 표현식으로 타입 결정
  1. () 안에 있는 표현식이 심볼의 이름만 있는 경우
    심볼의 선언을 통해서 심볼과 동일한 타입으로 결정
int main()
{
    int n = 10;
    int& r = n;
    int* p = &n;
    const int c = 10;
    const int& cr = c;

    decltype(n) d1; // int  d1
    decltype(r) d2; // int& d2			초기화 문제 error
    decltype(p) d3; // int* d3
    decltype(c) d4; // const int  d4	초기화 문제 error
    decltype(cr) d5;// const int& d5	초기화 문제 error
}
  • 함수의 반환 타입
    값 타입으로 반환하는 함수: 등호의 왼쪽에 올 수 없음 - lvalue가 될 수 없음
    참조 타입으로 반환하는 함수: 등호의 왼쪽에 올 수 있음 - lvalue가 될 수 있음
int x = 10;
int  foo() { return x; }
int& goo() { return x; }

int main()
{
	foo() = 20; // 10 = 20   error
	goo() = 20; // x의별명 = 20  ok
}
  1. () 안에 심볼 외에 연산자 등을 포함한 표현식이 있는 경우
    표현식의 결과가 등호 왼쪽에 올 수 있으면 참조 타입(&, lvalue reference)
    표현식의 결과가 등호 왼쪽에 올 수 없으면 값 타입
    표현식이 move()와 같이 rvalue reference를 반환하면 rvalue reference 타입(&&)

    괄호 안의 식을 함수라고 보고 그 결과에 따라 판단

int main()
{
    int n = 10;
    int* p = &n;
    
    decltype(p)  d1;    // int* d1;
    decltype(*p) d2;    // int& d2;  초기값 문제로 error

    decltype(n)    d3;  // int  d3;
    decltype(n+1)  d4;  // int  d4;
    decltype((n))  d5;  // int& d5;
    decltype(n=20) d6;  // int& d6;

    int x[3] = {1,2,3};
    
    decltype(x[0]) d7;  // int& d7; 
    auto a1 = x[0];     // int a1;

    decltype(x) d8;     // int d8[3]
    auto a2 = x;        // int a2[3] = x 에러이므로
                        // int* a2 = x;

    decltype(n++) d9;   // int
    decltype(++n) d10;  // int&
}

decltype(auto)

decltype(함수이름): 함수의 타입
decltype(함수호출식): 함수 실행의 결과 타입 (함수 반환 타입), 실제 함수가 호출되는 것은 아니고 함수 구현이 없어도 선언만 있으면 사용 가능 (Unevaluated Expression)

int x = 0;
int  foo(int a, int b);// { return 0;}
int& goo(int a, int b) { return x;}

int main()
{
    decltype(foo)      d1;	// int(int, int)
                            // int d1(int, int)
    decltype(foo(1,2)) d2;	// int
//	decltype(goo(1,2)) d3;	// int&
}
  • decltype(auto): auto 위치에 우변 표현식을 넣어서 타입 추론
    우변 표현식으로 타입을 추론하는데, 규칙은 decltype 규칙 사용 (C++14)
int main()
{
    auto ret1 = goo(1,2); // int ret1 = goo(1, 2)

    //decltype(goo(1,2)) ret2 = goo(1, 2);
    // int& ret2 = goo(1, 2)

    decltype(auto) ret2 = goo(1, 2);

    ret2 = 1000;
    std::cout << x << std::endl; // 1000
}
  • decltype(auto) 예제
/*
template<typename T> T Add(T a, T b) // 서로 다른 두 타입을 받아오지 못하는 한계
{
    return a + b;
}
*/
template<typename T1, typename T2> 
// decltype(a+b) Add(T1 a, T2 b)  // 1. 선언되지 않은 변수 사용하여 컴파일 에러
//auto Add(T1 a, T2 b) -> decltype(a+b) // 2. suffix return, 정확한 타입 반환
//auto Add(T1 a, T2 b) // 3. return문의 표현식을 보고 타입 추론, 참조 타입 반환 불가
decltype(auto) Add(T1 a, T2 b) // 4. 참조 타입 반환 가능 (C++14)
{
    return a + b;
}

int main()
{
    std::cout << Add(1, 2)   << std::endl;
    std::cout << Add(1, 2.3) << std::endl;
}

배열(Array)의 이름과 주소

배열의 이름은 배열의 주소가 아님;

  1. 변수의 주소를 구하려면 변수 이름 앞에 주소 연산자(&)를 붙임
  2. 포인터 변수를 선언하려면 변수 이름 앞에 * 연산자를 붙임
int main()
{
	int		n = 0;
    double	d = 0;
    
    // n과 d의 주소를 담은 포인터 변수 p1, p2 만들기
    int		*p1 = &n;
    double	*p2 = &d;
}
  • 배열의 주소를 구하려면
    규칙1과 2를 동일하게 적용
    연산자 우선순위를 높이기 위해 괄호 사용
int main()
{
	int x[3] = {1,2,3};
    
    // 배열 x의 주소를 담는 포인터 변수 p3 만들기
    int (*p3)[3] = &x; // 배열의 주소
}
  • 변수의 초기화
    변수는 동일 타입의 변수로 초기화(대입)될 수 있음
    배열을 동일 타입의 변수로 초기화(대입)될 수 없음 (메모리가 커 성능 문제)
int main()
{
	int n1 = 10;
    int n2 = n1;
    
    double d1 = 3.4;
    double d2 = d1;
    
    int x[3] = {1,2,3};
    int y[3] = x; // 컴파일 에러
}
  • Array To Point Conversion
    배열의 이름은 배열의 첫번째 요소의 주소로 암시적 형변환 됨
int main()
{
	int x[3] = {1,2,3};
	int *p = x; // 배열의 주소가 아닌 배열의 이름
}

p는 배열이 아닌 배열의 첫번째 요소를 가리킴

  • 배열의 주소와 배열 요소의 주소의 차이
int main()
{
	int x[3] = {1,2,3};
    int (*pa)[3] = &x; // 배열의 주소
	int 	 *pn = x; // 배열의 이름 - 배열의 첫번째 요소의 주소
    
    printf("%p\n", pa);
    printf("%p\n", pn);
}

pa와 pn의 출력값이 같아 동일해보임

  1. pa와 pn의 차이점 1
    +1 연산 결과가 다름
int main()
{
	...
    printf("%p\n", pa+1); // 12byte 증가 sizeof(int[3])
    printf("%p\n", pn+1); // 4byte 증가 sizeof(int)
}
  1. pa와 pn의 차이점 2
    *pa: 배열을 가리키는 포인터이므로 *를 하면 배열이 나옴
    *pn: int를 가리키는 포인터이므로 *를 하면 int가 나옴
int main()
{
	...
    // * 연산
    int n1 = *pn; // *(int주소) -> int
    int *p2 = *pa; // *(배열주소) -> 배열 -> 첫번째 요소의 주소로 형변환
    
    int n2 = **pa;
}
  • 다차원 배열의 주소와 이름
int x[2] = {1,2};
// 1차 배열의 주소
int (*p1)[2] = &x;

int y[3][2] = {{1,2},{3,4},{5,6}};
int (*p2)[3][2] = &y; // 2차원 배열의 주소
int (*p3)[2] = y; // 배열의 이름 -> 배열의 첫번째 요소의 주소
				  // 1차원 배열의 주소
int *p4 = *y; // *(1차원 배열의 주소) -> 1차원 배열 -> 첫번째 요소의 주소

Quiz. 물음표 채워보기

int z[2][2][2] = {1,2,3,4,5,6,7,8};
? p5 = &z;
? p6 = z;
? p7 *z;

예상

int z[2][2][2] = { {{1,2},{3,4}}, {{5,6},{7,8}} };
int (*p5)[2][2][2] = &z;
int (*p6)[2][2] = z;
int (*p7)[2] = *z;
profile
주니어 언리얼 프로그래머

0개의 댓글