반응형

일반화된 복사 생성자

  • 클래스 템플릿의 멤버 함수 템플릿으로 구현하는 복사 생성자.

  • U(int) T(double)로 복사(대입) 가능하다면 Complex<U> Complex<T>에 복사(대입) 가능해야 한다.

  • friend class로 선언되어야 한다.

template<typename T> class Complex
{
    T re, im;
public:
    Complex(T a = {}, T b = {}) : re(a), im(b) {}
    T getReal() const;
    static int cnt;
    template<typename U> T func(const U& c);

    // 일반화된 복사 생성자 선언
    template<typename U>
    Complex(const Complex<U>& c);

    template<typename> friend class Complex;
};

// 일반화된 복사 생성자 구현부
template<typename T> template<typename U>
Complex<T>::Complex(const Complex<U>& c)
    : re(c.re), im(c.im)
{

}

int main()
{
    Complex<int> c1(1, 1);
    Complex<int> c2 = c1;
    Complex<double> c3 = c1;
}
반응형

반응형

템플릿과 타입의 차이

  • Complex: 템플릿 틀 자체

  • Complex<T>: 실제 타입

  • 멤버 함수안에서는 

멤버 함수안에서는 Complex<T> 대신 Complex를 사용할 수 있다.

template<typename T> class Complex
{
    T re, im;
public:
    void foo(Complex c) // 멤버 함수에서는 OK(Complex<T> 동급으로 취급)
    {

    }
};

void foo(Complex c) // 일반 함수에서는 Error
{

}

void foo(Complex c)
{
    Complex c1; // Error
    Complex<int> c2; // OK
}

 

템플릿 관련 표기법

  • 디폴트 값 표기

    • int a = 0;

    • T a = T(); // C++ 98/03

    • T a = {};  // C++11

  • 멤버 함수를 외부에 표기

  • static memeber data 외부 표기

  • 클래스 템플릿의 멤버 함수 템플릿 표기

template<typename T> class Complex
{
    T re, im;
public:
	// 디폴트 값 표기
    Complex(T a = {}, T b = {}) : re(a), im(b) {}
    T getReal() const;
    static int cnt;

    template<typename U> T func(const U& c);
};

// 클래스 템플릿의 멤버 함수 템플릿 구현
template<typename T> template<typename U>
T Complex<T>::func(const U& c)
{

}

// static 멤버 데이터 외부 구현
template<typename T>
int Complex<T>::cnt = 0;

// 멤버 함수 외부 구현
template<typename T> 
T Complex<T>::getReal() const
{
    return re;
}

int main()
{
    Complex<int> c2;
}
반응형

반응형

배열의 특징

  • 자신과 동일한 타입으로 초기화 될 수 없다.
  • 배열의 이름은 배열의 첫번째 요소의 주소로 암시적 형 변환 된다.
  • 배열을 가리키는 참조를 만들 수 있다.

함수 템플릿을 만들때

  • 배열을 값으로 받으면 T는 요소 타입의 포인터로 결정된다.
  • 배열을 참조로 받으면 T는 배열 타입으로 결정된다.
#include <iostream>

template<typename T>
void foo(T a)
{

}

template<typename T>
void goo(T& a)
{

}

int main()
{
    int x[3] = { 1,2,3 };

    foo(x); 
    goo(x);
}

 

관련 예제

  • 문자열의 타입 : char 배열
  • 문자열을 값으로 받으면 T는 const char* 결정되고, 참조로 받으면  const char[]로 결정된다.
  • 크기가 다른 배열은 다른 타입이다.
#include <iostream>

template<typename T>
void foo(T a, T b)
{

}

template<typename T>
void goo(T& a, T& b)
{

}

int main()
{
    foo("orange", "apple"); // ok
    goo("orange", "apple"); // error
}

 

반응형

반응형

배열의 이름과 주소

일반적으로 데이터 타입을 뺀 나머지는 변수명이라 할 수 있다.

int i에서 int를 빼면 i가 변수명이다. 그리고 데이터 타입에 *붙여서 선언하면 주소를 담을 수 있는 포인터 변수를 선언 할 수 있다. 그러면 배열을 어떨까?

 

int x[3] 에서 int[3] 뺀 나머지인 x가 동일하게 변수명이다. 그러면 포인터 변수는 마찬가지로 x 변수에 *만 붙이면 배열의 포인터 주소가 되는 것일까?

 

대부분 배열의 주소는 첫번째 요소의 주소로 많이 알고 있다. 하지만 배열의 주소와 첫번째 요소의 주소는 같으면서도

다르므로 명확히 구분해서 사용할 필요가 있다.

#include <iostream>

int main()
{
    int i = 1;
    int *px = &i;

    int x[3] = { 1, 2, 3 };
    int *p1 = x; // 일반적으로 생각하는 배열의 주소(묵시적으로 첫번째 요소의 주소로 형변환 됨)
    int(*p2)[3] = &x; // 정확한 배열의 주소(요소가 모두 포함된 전체 컨테이너의 주소)

    // + 1 연산을 하면 기본적으로 데이터 타입 사이즈 만큼 증가된 메모리 주소를 가르침
    printf("%p, %p\n", p1, p1 + 1); // 배열의 첫번째 요소의 주소에서 데이터 사이즈인 4바이트 증가
    printf("%p, %p\n", p2, p2 + 1); // 요소가 3개인 배열의 주소에서 데이터 사이즈인 12바이트 증가
}

 

반응형

반응형

Template Argument Type Deduction

  • 컴파일러가 함수 인자를 보고 템플릿의 타입을 결정하는 것을 말한다.
  • 함수 인자의 타입과 완전히 동일한 타입으로 결정되지는 않는다.
#include <iostream>
using namespace std;

// 함수 템플릿 인자가 값 타입(T a) 일때
template<typename T> void foo(T a)
{
    ++a;
}

int main()
{
    int n = 0;
    int& r = n;
    const int c = n;
    const int& cr = c;

    foo(n);  // T : int
    foo(c);  // T : const int ?
    foo(r);  // T : int& ?
    foo(cr); // T : const int& ?
}

Template Argument Type Deduction 원리 1

  • 템플릿 인자가 값 타입일때 (T a)
    • 함수 인자가 가진 const, volatile, reference 속성을 제거하고 T의 타입을 결정한다.
    • 주의 - 인자가 가진 const 속성만 제거 된다.
#include <iostream>
using namespace std;

// 함수 템플릿 인자가 값 타입(T a) 일때
template<typename T> void foo(T a)
{
}

int main()
{
    int n = 0;
    int& r = n;
    const int c = n;
    const int& cr = c;

    foo(n);  // T : int
    foo(c);  // T : int
    foo(r);  // T : int
    foo(cr); // T : int
    
    const char* s1 = "hello";
    foo(s1); // T : char const* // s1이 아닌 char*가 const 이므로 제거 되지 않음
    
    const char* const s2 = "hello";
    foo(s2); // T : char const*  // s2가 const 이므로 인자에 대한 const만 제거됨
}

Template Argument Type Deduction 원리 2

  • 템플릿 인자가 참조 타입일때 (T& a)
    • 함수 인자가 가진 reference 속성을 제거하고 T의 타입을 결정한다.
    • const, volatile 속성은 유지한다.
#include <iostream>
using namespace std;

// 함수 템플릿 인자가 참조 타입(T& a) 일때
template<typename T> void foo(T& a)
{
    ++a;
}


int main()
{
    int n = 0;
    int& r = n;
    const int c = n;
    const int& cr = c;

    foo(n);  // T : int
    foo(c);  // T : const int
    foo(r);  // T : int
    foo(cr); // T : const int
}

 

Template Argument Type Deduction 정리

  • 템플릿 인자가 값 타입(T a)
    • 함수 인자가 가진 const, volatile, reference 속성 제거 후 T 타입 결정
    • 인자의 const 속성만 제거
  • 템플릿 인자가 참조 타입(T& a)
    • 함수 인자가 가진 reference 속성만 제거 후 T 타입 결정
    • const, volatile 속성 유지
    • 인자가 (const T& a)경우 const를 제거하고 T 타입 결정
  • 템플릿 인자가 forwarding 레퍼런스 타입(T&& a)
    • lvalue, rvalue 모두 전달 받음
  • 템플릿 인자가 배열
    • argument decay 발생
반응형

반응형

std::typeid 활용

  • C++ 표준의 typeid() 연산자 사용
    • const, volatile, reference 구분하여 조사할 수 없음
#include <iostream>
using namespace std;

template<typename T> void foo(const T a)
{
    cout << "T : " << typeid(T).name() << endl;
    cout << "a : " << typeid(a).name() << endl;
}

int main()
{
    foo(3);
    foo(3.3);
}

결과

int
int
double
double

 

boost:type_index 활용

  • boost::type_id_with_scv<T>().pretty_name() 사용
  • const, volatile, reference 구분하여 조사할 수 있음
  • <boost/type_index.hpp>
  • namespace boost::typeindex 안에 포함
  • 변수의 타입을 조사 할때는 decltype()을 이용
    • type_id_with_cvr<decltype(a)>().pretty_name()
  • boost 사용법 참고 : https://www.devoops.kr/77
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
using namespace boost::typeindex;

template<typename T> void foo(const T a) // 실제 템플릿 타입 T와 변수타입은 다를 수 있음
{
    //cout << "T : " << typeid(T).name() << endl; // int, double
    //cout << "a : " << typeid(a).name() << endl; // int, double : std의 typeid로는 구분 불가
    cout << type_id_with_cvr<T>().pretty_name() << endl;
    cout << type_id_with_cvr<decltype(a)>().pretty_name() << endl;
}

int main()
{
    foo(3);
    foo(3.3);
}

결과

int
int const
double
double const

 

 

 

반응형

반응형

boost 설치

아래의 boost 사이트에 접속하여 런타임 플랫폼에 맞는 설치 파일 다운로드

https://www.boost.org/users/download/

압축파일을 적당한 위치에 해제한다

대부분의 기능은 헤더파일 참조로 사용 가능하지만 일부 기능은 사전 빌드가 필요하므로 제공하는 배치 파일과 빌드 실행파일을 이용하여 빌드를 진행하자.

  • 빌드 실행 파일 생성 : bootstrap.bat(실행 시 b2.exe 생성됨)
  • 빌드 실행 : b2.exe(성공적으로 빌드가 완료되면 stage/lib 디렉토리에 다양한 파일이 생성됨)

비주얼 스튜디오 > 프로젝트 > 속성

속성 > VC++ 디렉터리 > 일반 > 포함 디렉터리 > 편집

포함 디렉터리 > 줄 추가 > ...찾기 > boost 폴더 선택

속성 > VC++ 디렉터리 > 일반 > 라이브러리 디렉터리 > 편집

라이브러리 디렉터리 > 줄 추가 > ...찾기 > boost 폴더/stage/lib 선택

 

이제 boost 라이브러리에 포함된 기능을 include하여 사용하자

#include "pch.h"
#include <iostream>
#include <boost/any.hpp> // 관행상 boost 폴더를 포함
using namespace std;

int main()
{
    boost::any a1 = 1;
    boost::any a2 = 1.1;
    boost::any a3 = "aaa";

    const char* s = boost::any_cast<const char*>(a3);
}

 

 

 

 

 

 

 

 

반응형

반응형
  • Lazy Instantiation
    • 사용되지 않은 템플릿 클래스는 인스턴스화 되지 않음
    • 사용되지 않은 템플릿 클래스의 멤버 함수도 인스턴스화 되지 않음
    • 사용되지 않은 템플릿 클래스의 static 함수도 인스턴스화 되지 않음
    • 사용되지 않은 전역 템플릿 변수도 인스턴스화 되지 않음
template<typename T> class A
{
    T data;
public:
    void foo(T n) { *n = 10; } // 참조 오류 코드
};

int main()
{
    A<int> a;
    // a.foo(1); 템플릿의 멤버 함수가 호출되지 않는다면 인스턴스화 되지 않으므로 컴파일 성공
}
struct Resource1
{
    Resource1() { cout << "Resource1()" << endl; }
    ~Resource1() { cout << "~Resource1()" << endl; }
};

struct Resource2
{
    Resource2() { cout << "Resource2()" << endl; }
    ~Resource2() { cout << "Resource2()" << endl; }
};

template<typename T> struct Test
{
    Resource1 res1; // 클래스내 메모리 할당이 필요하므로 인스턴스화됨
    static Resource2 res2; // static 멤버는 별도 메모리 할당으로 인스턴스화 되지 않음
};
template<typename T> Resource2 Test<T>::res2; // 사용되지 않아서 인스턴스화 되지 않음

int main()
{
    cout << "main" << endl; // 1. 메인 출력
    Test<int> t; // 2. Resource1 생성자 출력
}

 

  • if 문과 Lazy Instantiation
    • if 문은 "실행시간 조건문"으로 컴파일 시간에 조건이 false로 결정되어도 if 문에 있는 코드는 항상 사용되는 것으로 간주함
    • C++17 if constexpr는 "컴파일 시간 조건문"으로 조건이 false로 결정되면 if 문에 포함된 코드는 사용되지 않는 것으로 간주됨
    • 동일한 이름의 함수가 여러개 있을 때 어떤 함수를 호출할 지 결정하는 것은 컴파일 시간에 결정되므로 선택되지 않은 함수가 템플릿이라면 인스턴스화 되지 않음
template<typename T> void foo(T n)
{
    *n = 10; // 참조 오류 코드
}

int main()
{
    if(false) // 런타임 조건식이므로 foo는 인스턴스화 됨
        foo(0);

    if constexpr ( false ) // C++17의 컴파일타임 조건식이므로 foo는 인스턴스화 되지 않음
        foo(0);
}
반응형

+ Recent posts