반응형
  • C++17 이전까지는 클래스 템플릿은 함수 인자를 통해 타입을 추론 할 수 없기 때문에, 클래스 템플릿 사용이 복잡하고 불편함
  • "클래스 템플릿의 객체를 생성하는 함수 템플릿"을 사용
    • 함수 템플릿은 함수 인자를 통해서 컴파일러가 추론(Argument Type Deduction)할 수 있는 특징 활용 기법
  • make_pair(), make_tuple()등의 make 계열 함수, STL 삽입반복자등
#include <iostream>

template<typename T> void foo(T a) {}
template<typename T, typename U> struct pair
{
    T first;
    U second;
    pair(const T& a, const U& b) : first(a), second(b) {}
};

template<typename T, typename U>
pair<T, U> make_pair(const T& a, const U& b)
{
    return pair<T, U>(a, b);
}

int main()
{
    pair<int, double> p(1, 3.4);
    foo(p);
    foo(pair<int, double>(1, 3.4)); // 클래스 템플릿은 꼭 타입 전달 필요
    foo(make_pair(1, 3.4)); // 함수 템플릿으로 타입 추론하여 클래스 객체 생성
}
반응형

반응형

템플릿 인자 타입 추론(Template Argument Type Deduction)

  • 사용자가 템플릿 인자를 명시적으로 지정하지 않은 경우 컴파일러가 인자를 보고 추론(deduction)하는 것
  • 타입을 결정하는 방식(type deduction rule  참고)
template<typename T> T square(T a)
{
    return a * a;
}

int main()
{
    square<int>(3);
    square(3); // 인자 타입 추론(int)
    square(3.3); // 인자 타입 추론(double)
}

 

클래스 템플릿 인자 타입 추론(Class Template Argument Type Deduction)

  • C++ 17부터 지원
  • 생성자를 통한 타입 결정
  • 사용자가 "user define deduction guide" 제공
#include <list>
using namespace std;

template<typename T> class Vector
{
    T* buff;
public:
    Vector() {} // #1 타입 추론이 불가능 할경우 유저 정의 추론 가이드 필요
    Vector(int sz, T initValue) {}

    template<typename C> Vector(C& c) {} // #2 타입 추론이 불가능 할경우 유저 정의 추론 가이드 필요
};

//유저 정의 추론 가이드 제공 필요(user define deduction guide)
Vector()->Vector<int>; // #1 Vector 기본 생성자 호출 시 int로 타입 추론 가이드
template<typename C> Vector(C& c)->Vector<typename C::valuetype >; // #2 int

int main()
{
    Vector<int> v1(10, 3);

    list<int> s = { 1,2,3 }; // list s = { 1,2,3 }; C++17 Style

    Vector v4(s);
}
반응형

반응형

Template Instantiation

  • 컴파일러가 함수(클래스) 틀(Template)로 부터 실제 C++ 함수(클래스)를 만들어 내는 과정
template<typename T>
T square(T a) // template 기준 int squre(int a), double square(double a) 실제 함수를 만드는 과정
{
    return a * a;
}

int main()
{
    square(3);
}

 

  • 명시적 인스턴스화(Explicit Instantiation)
    • 템플릿을 사용해서 특정 타입의 함수(클래스)를 생성해 달라고 명시적으로 지시하는 것
    • 함수/클래스 선언과 유사한 모양으로 작성
template<typename T> class Test
{
public:
    void foo() {}
    void goo() {}
};
template class Test<int>; // 명시적 인스턴스화(클래스 템플릿), foo, goo 모두 인스턴스화
template void Test<int>::foo(); // 명시적 인스턴스화(클래스 템플릿), foo 부분 인스턴스화

template<typename T> T square(T a)
{
    return a * a;
}
template int square<int>(int); // 명시적 인스턴스화(함수 템플릿)
template int square<>(int); // 명시적 인스턴스화(템플릿) 축약 표현
template int square(int); // 명시적 인스턴스화(템플릿) 축약 표현

int main()
{
}

 

  • 암시적 인스턴스화(Implicit Instantiation)
    • 명시적 인스턴스화를 하지 않고 템플릿을 사용하는 경우, 암시적으로 인스턴스화가 발생함
    • 사용 방법
      • 사용자가 타입을 직접 전달하는 방법
      • 함수 인자를 통해서 컴파일러가 타입을 추론하는 방법(template argument type deduction)
      • 클래스 생성자를 통한 컴파일러 타입 추론하는 방법(class template type deduction, C++17이상)
    • 클래스 템플릿의 경우 사용하지 않는 멤버 함수는 인스턴스화 되지 않음(lazy instantiation)
template<typename T> class Test
{
public:
    void foo() {}
    void goo() {}
};
//template class Test<int>; // 명시적 인스턴스화(클래스 템플릿), foo, goo 모두 인스턴스화

template<typename T> T square(T a)
{
    return a * a;
}
//template int square<int>(int); // 명시적 인스턴스화(함수 템플릿)

int main()
{
    int n1 = square(3); // 암시적 인스턴스화(인자를 통한 컴파일러 타입 추론)
    int n2 = square<int>(3); //암시적 인스턴스화

    Test<int> t1; // 암시적 인스턴스화(클래스)
    t1.foo(); // Test 클래스의 foo 멤버함수만 사용하므로 foo 멤버함수만 인스턴스화됨(goo x)
}

 

  • 어셈블리 코드 확인
    • cl file.cpp /FAs -> file.asm 파일 생성
    • g++ file.cpp -S -> file.s 파일 생성
  • 컴파일러 옵션 사용 g++, clang++(중간 언어로 확인 가능)
    • g++ -fdump-tree-original file.cpp -> file.cpp.003t.original
    • clang++ -Xclang -ast-print -fsyntax-only file.cpp
반응형

반응형

아래의 Complex 멤버의 data type의 예

  • int로 할 경우 : 실수를 담을 수 없음
  • double로 할 경우 : 실수와 정수를 모두 담을 수 있지만 double은 int보다 overhead가 있음
  • 라이브러리 설계자는 Complex의 틀(template)만 제공하고 내부 타입은 사용자가 결정할 수 있도록 제공
template<typename T>
class Complex
{
    T re, im;
public:
    Complex(T r, T i) : re(r), im(i) {}
};

int main()
{
    Complex<int> c1(1, 2);
    Complex<double> c2(1.1, 2.2);
}
반응형

반응형

함수 또는 클래스의 틀(template)을 제공하는 방법

 

  • 매크로(macro) 사용
    • 전처리기(pre-processor)가 코드를 생성
    • 전처리기는 함수 인자를 통해서 타입을 추론 할 수 없음
// 함수를 만드는 틀
#define MAKE_SQUARE( T )    \
T square(T a)               \
{                           \
    return a * a;           \
}
// 틀에 타입을 전달해서 함수를 생성한다.
MAKE_SQUARE(int)
MAKE_SQUARE(double)

int main()
{
    square(3);
    square(3.3);
}
  • 템플릿(template) 사용
    • 컴파일러가 코드를 생성
    • 컴파일러는 함수 인자를 통해서 타입을 추론할 수 있음
template<typename T> // typename 대신 class로 사용해도 됨
T square(T a)
{
    return a * a;
}

int main()
{
    square<int>(3); // int square(int) 생성
    square<double>(3.3); // double square(double) 생성

    square(3); // 인자 타입 추론으로 
    square(3.3); // 
}

 

반응형

반응형

파일 입출력

  • <fstream>
  • ofstream : 출력파일을 위한 스트림
#include <iostream>
#include <fstream>

int main()
{
    // ostream cout => basic_ostream<>
    // typedef basic_ostream<char> ostream;
    std::cout << "hello";
    std::ofstream f("a.txt"); // 파일 출력 객체 생성
    f << "hello"; // a.txt에 출력
}
  • ifstream : 입력 파일을 위한 스트림
#include <iostream>
#include <string>
#include <fstream>

int main()
{
    std::string s;
    std::ifstream fin("ConsoleApplication1.cpp"); // 입력 파일 스트림
    fin >> s; // 입력 파일 스트림의 첫번째 단어 저장
    std::cout << s << std::endl; // 출력
}
  • 파일 내용 전체 출력을 위한 STL 응용
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <algorithm>

int main()
{
    std::string s;

    std::ifstream fin("ConsoleApplication1.cpp"); // 입력 파일 스트림

    std::vector<std::string> v; // 벡터 객체 생성

    while (std::getline(fin, s)) // 파일의 끝까지 getline으로 전체 읽기
    {
        v.push_back(s); // 벡터의 아이템으로 추가
    }

    for(auto& n :v) // 벡터 전체 loop
        std::cout << n << std::endl; // 벡터의 아이템 전체 출력
}
반응형

반응형

std::cin

  • 입력값 확인 및 재입력 처리
#include <iostream>

int main()
{
    int n = 0;

    while(1)
    {
        std::cin >> n; //숫자 입력을 기대하였으나 사용자가 문자를 입력 하였을때

        if (std::cin.fail()) // 정상적인 입력 여부 확인
        {
            std::cout << "실패" << std::endl;
            std::cin.clear(); // cin의 상태를 나타내는 비트 초기화
            std::cin.ignore(256, '\n'); // 내부적으로 사용되는 입력 버퍼 제거, \n만날때까지 최대 256자 제거
            continue;
        }
        break;
    }

    std::cout << n << std::endl;
}

std::cin

  • 1개의 단어 입력 처리
#include <iostream>
#include <string>

int main()
{
    std::string w;
    std::cin >> w; // 1개의 단어 입력
        
    std::cout << w << std::endl;
}
  • 문장(다중 단어) 입력 처리
#include <iostream>
#include <string>

int main()
{  
    std::string s;
    std::getline(std::cin, s); // 문장 전체 입력

    std::cout << s << std::endl;
}
반응형

반응형

예외(Exception) 기본

  • throw, try, catch
  • throw를 사용해서 예외를 던짐
  • throw된 예외는 반드시 잡아서 처리해야 하며, 처리하지 않은 경우 abort() 함수를 수행하고 종료됨
  • try ~catch 구문을 사용해서 예외 처리

Catch(...)

  • 모든 종류의 예외를 잡을 수 있음
  • catch를 여러개 만들때는 반드시 마지막에 놓아야 함
#include <iostream>

int foo()
{
    if (1) // 예외조건 가정
        throw 1;

    return 0;
}

int main()
{
    try
    {
        foo();
    }
    catch (int n)
    {
        std::cout << "예외 발생" << std::endl;
    }
    catch (...)
    {
        std::cout << "... 예외 발생" << std::endl;
    }
    std::cout << "계속실행" << std::endl;
}

std::exception

  • C++ 표준 예외의 최상위 클래스
  • what() 가상함수 제공
#include <iostream>

class MemoryException : public std::exception // 예외 클래스 정의, std::exception 상속
{
public:
    virtual const char* what() const noexcept // std::exception의 가상함수 what() 구현
    {
        return "메모리 예외";
    }
};

int foo()
{
    if (1) // 예외조건 가정
        throw MemoryException(); // 사용자 정의 예외 클래스 throw

    return 0;
}

int main()
{
    try
    {
        foo();
    }
    catch (MemoryException & e)
    {
        std::cout << e.what() << std::endl;
    }

    std::cout << "계속실행" << std::endl;
}

C++ 표준 예외 클래스

  • std::bad_alloc
  • std::range_error
  • std::out_of_range
#include <iostream>

int main()
{
    try
    {
        int* p = new int[100]; // C++ 메모리 할당 실패시 예외 발생
    }
    catch (std::bad_alloc & e)
    {
        std::cout << e.what() << std::endl;
    }
}

noexcept

  • 함수에 예외가 없음을 명시적으로 표기하는 방법
  • throw() 동일 역할
  • 명시 할경우 컴파일러 최적화시에 도움됨
  • noexcept() 함수로 예외 없음 여부를 조사 할 수 있음
#include <iostream>

// 예외가 없는 함수
void a() noexcept {}
void a1() throw() {}

int main()
{
    bool ba = noexcept( a() ); //예외 없음 여부 조사 가능
    bool ba1 = noexcept( a1() );
    std::cout << ba << "," << ba1 << std::endl;
}
반응형

+ Recent posts