반응형

가변인자 템플릿(Variadic template)를 활용하여 tuple 전체 요소를 출력

 

#include <iostream>
#include <tuple>
using namespace std;

//튜플과 index_sequence를 받아서 튜플 전체 요소를 출력
template<typename TP, size_t ... I> 
void print_tuple_imp(const TP& tp, const index_sequence<I...>&)
{
    int x[] = { get<I>(tp)... };

    for (auto& n : x)
        cout << n << ", ";
}

template<typename TP>
void print_tuple(const TP& tp)
{
    // 튜플 사이즈를 구해서 index_sequence를 생성하여 print_tuple_imp에 전달
    print_tuple_imp(tp, make_index_sequence<tuple_size<TP>::value>());
}

int main()
{
    tuple<int, int, int> tp(1, 2, 3);

    print_tuple(tp);
}
반응형

반응형

기반 클래스의 멤버에 접근하는 방법(기반 지식)

  • 기반 클래스의 멤버와 파생클래스의 멤버의 이름이 동일할때는 자신(파생 클래스)의 멤버가 우선

  • 기반 클래스의 멤버에 접근하는 방법

    • d.Base::value

    • static_cast<Base&>(d).value;

  • 값 캐스팅과 참조 캐스팅

    • static_cast<Base>(d): 임시객체 생성. lvalue가 될 수 없음

    • static_cast<Base&>(d): 임시객체를 생성 안함. lvalue가 될 수 있음

#include <iostream>
using namespace std;

struct Base
{
    int value = 10;
};

struct Derived : public Base
{
    int value = 20;
};

int main()
{
    Derived d;

    cout << d.value << endl; // 20
    cout << d.Base::value << endl; // 10

    cout << static_cast<Base>(d).value << endl; // 10 임시객체 생성
    cout << static_cast<Base&>(d).value << endl; // 10 참조

    //static_cast<Base>(d).value = 30; // Error
    static_cast<Base&>(d).value = 30; // OK

}

 

Tuple_element

  •  tuple이 가진 N번째 요소의 타입을 구하는 템플릿

  • 메인 템플릿(Main template) 생성, 구현은 불필요함으로 생략

  • 0번째 요소를 구하는 부분 특수화 구현

  • N번째 요소를 구하는 부분 특수화 구현(Recursive)

#include <iostream>
using namespace std;

// tuple 메인 템플릿
template<typename ... Types> struct xtuple
{
    static constexpr int N = 0;
};

// tuple 부분 특수화
template<typename T, typename ... Types> 
struct xtuple<T, Types...> : public xtuple<Types...> // 타입을 상속을 통해 재귀 저장 효과
{
    T value;
    xtuple() = default; // 기본 생성자
    xtuple(const T& v, const Types& ... args) 
        : value(v), xtuple<Types...>(args...) {} // Pack expansion으로 가변인자 전달

    static constexpr int N = xtuple<Types...>::N + 1;
};

// 메인 템플릿
template<size_t N, typename TP> struct xtuple_element;

// 요소의 타입을 구할 수 있도록 부분 특수화
template<typename T, typename... Types> struct xtuple_element<0, xtuple<T, Types...>>
{
    typedef T type;
    typedef xtuple<T, Types...> tupleType;
};

// 요소의 타입을 구할 수 있도록 부분 특수화
template<size_t N, typename T, typename... Types> 
struct xtuple_element<N, xtuple<T, Types...>>
{
    typedef typename xtuple_element<N-1, xtuple<Types...>>::type type;
    typedef typename xtuple_element<N-1, xtuple<Types...>>::tupleType tupleType;
};

// xtuple 요소를 꺼내는 함수
template<size_t N, typename TP>
typename xtuple_element<N, TP>::type& xget(TP& tp)
{
    return static_cast<typename xtuple_element<N, TP>::tupleType&>(tp).value;
}

int main()
{
    xtuple<int, double, char> t3(1, 3.4, 'A'); // 1
    
    cout << xget<1>(t3) << endl; // 3.4
    xget<1>(t3) = 1.1;
    cout << xget<1>(t3) << endl; // 1.1
}
반응형

반응형

tuple C++11 표준

  • 서로 다른 타입의 객체를 N개 보관하는 템플릿
#include <iostream>
#include <tuple>
using namespace std;

int main()
{
    tuple<> t0;
    tuple<int> t1(1);
    tuple<int, double, int, char> t4(1, 3.4, 2, 'A');

    get<2>(t4) = 15; // 참조 리턴으로 값 변경 가능

    cout << get<2>(t4) << endl; // 2
}

tuple 만들기

  • 가변인자 템플릿을 사용해서 메인 템플릿(Main template)을 만듬

  • 1개의 요소를 보관할 수 있도록 부분 특수화 구현

  • 상속을 사용해서 N개를 보관할 수 있게 구현

  • 생성자등 필요한 멤버 추가

#include <iostream>
using namespace std;

// tuple 메인 템플릿
template<typename ... Types> struct xtuple
{
    static constexpr int N = 0;
};

// tuple 부분 특수화
template<typename T, typename ... Types> 
struct xtuple<T, Types...> : public xtuple<Types...> // 타입을 상속을 통해 재귀 저장 효과
{
    T value;
    xtuple() = default; // 기본 생성자
    xtuple(const T& v, const Types& ... args) 
        : value(v), xtuple<Types...>(args...) {} // Pack expansion으로 가변인자 전달

    static constexpr int N = xtuple<Types...>::N + 1;
};

int main()
{
    xtuple<> t0;
    xtuple<int> t1(3);
    xtuple<int, double, char> t4(1, 3.4, 'A');
}
반응형

반응형

함수의 인자 타입을 구하는 traits 만들기

  • 메인 템플릿(Main template)만들고 typedef T type 제공

  • 함수 타입인 T 안에 있는 함수 인자 타입을 얻을 수 있도록 부분 특수화 

    • T -> R(A1, A2), T -> R(A1, Types...)

  • N번째 인자 타입 구하는 방법

    • 0번째 인자의 타입을 구하는 부분 특수화 작성

    • N번째 인자의 타입을 구하는 부분 특수화 작성

      • N == 0이 될때까지 Recursive 사용

      • Recuresive 사용시, 함수의 0번째 인자를 제거하고 N-1 사용

        • argument_type<3, R(A1, A2, A3, A4))::type

          • argument_type<2, R(A1, A2, A3, A4))::type

            • argument_type<1, R(A1, A2, A3, A4))::type

              • argument_type<0, R(A1, A2, A3, A4))::type

#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;

double hoo(short a, int b, double c) { return 0; }

// 메인 템플릿
template<size_t N, typename T> struct argument_type
{
    typedef T type;
};

// 0번째 인자의 타입을 구하는 부분 특수화 작성
template<typename T, typename A1, typename ... Types> 
struct argument_type<0, T(A1, Types ...)>
{
    typedef A1 type;
};

// N번째 인자의 타입을 구하는 부분 특수화 작성
template<size_t N, typename R, typename A1, typename ... Types> 
struct argument_type<N, R(A1, Types ...)>
{
    typedef
        typename argument_type<N - 1, R(Types...)>::type type;
};

template<typename T> void foo(const T& t)
{
    // T : double(short, int, double)
    typename argument_type<0, T>::type ret;
    cout << typeid(ret).name() << endl;
}

int main()
{
    foo(hoo);
}
반응형

반응형

함수 정보를 구하는 traits 만들기

  • 메인 템플릿(Primary template)을 만들고 typedef T type 제공

  • 함수 타입의 T안에 있는 리턴 타입을 얻을 수 있도록 부분 특수화 구현

  • 부분 특수화를 할땡 가변인자 템플릿을 활용

#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;

double hoo(short a, int b) { return 0; }

// 메인 템플릿
template<typename T>
struct result_type
{
    typedef T type;
};

// 함수 타입에 대한 부분 특수화, 가변인자 활용
template<typename T, typename ... Types>
struct result_type<T(Types ... args)>
{
    typedef T type;
};

template<typename T> void foo(const T& t)
{
    // T : double(short, int) 함수 모양
    typename result_type<T>::type ret;
    cout << typeid(ret).name() << endl;
}

int main()
{
    foo(hoo);
}

 

traits 잘못 사용할때 처리 방법

  • 메인 템플릿에서 typedef T type을 제공하지 않는 방법

  • 메인 템플릿에서 static_assert()를 사용해서 명시적 에러 메시지 출력

  • 메인 템플릿의 선언만 구현

#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;

double hoo(short a, int b) { return 0; }

// 1. typedef T type 미제공
template<typename T>
struct result_type
{
};

// 2. static_assert()를 사용해서 에러 메시지 출력
template<typename T>
struct result_type
{
    static_assert(is_function<T>::value, "Not supported type");
};

// 3. 선언부만 제공
template<typename T>
struct result_type;


// 함수 타입에 대한 부분 특수화
template<typename T, typename ... Types>
struct result_type<T(Types ... args)>
{
    typedef T type;
};

template<typename T> void foo(const T& t)
{
    // T : double(short, int) 함수 모양
    typename result_type<T>::type ret;
    cout << typeid(ret).name() << endl;
}

int main()
{
    foo(hoo);
}
반응형

반응형

Parameter Pack 각 요소 꺼내기

  • Pack Expansion -> array or tuple에 담기
#include <iostream>
#include <tuple>
using namespace std;

template<typename ... Types> 
void foo(Types ... args)
{
    // 각각의 타입별 값을 tuple 개별 요소로 추가
    tuple<Types...> tp(args...);

    cout << get<0>(tp) << endl;
    cout << get<1>(tp) << endl;
    cout << get<2>(tp) << endl;
}


int main()
{
    foo(1, 3.4, "AA");
}

 

  • 재귀 호출과 유사한 호출식을 사용하기
    • 1번째 인자는 이름 있는 변수 2번째 인자는 가변 인자로 구현
#include <iostream>
#include <tuple>
using namespace std;

void foo() {} // 재귀 호출 종료용

template<typename T, typename ... Types>
void foo(T value, Types ... args)
{
    cout << value << endl;
    foo(args...); 
    // 아래처럼 반복 호출 됨
    // foo(3.4, "AA");
    // foo("AA");
    // foo();
}


int main()
{
    foo(1, 3.4, "AA"); // value : 1, args : 3.4, "AA"
}

 

 

fold expression(C++17)

  • 이항 연산자를 사용해서 parameter pack안에 있는 요소에 연산을 수행하는 문법
  • parameter pack의 이름에서 ... 붙이지 않고 사용
    • pack expansion : args...
    • fold expression : args + ...
  • 4가지 형태(args:1,2,3,4)
    • unary right fold: ( args op ... )
      • (args + ... ) // 1+(2+(3+4))
    • unary left fold : (... op args)
      • (... + args) // ((1+2)+3)+4
    • binary right fold : ( args op ... op init)
      • ( args + ... + 10) // 1+(2+3(4+10)))
    • binary left fold : ( init op ... op args)
      • (10 + ... + args) // (((10+1)+2)+3)+4
#include <iostream>
using namespace std;

template<typename ... Types>
void foo(Types ... args)
{

    // args 값을 모두 호출할 수 있음
    //binary left fold : ( init op ... op args)
    (cout << ... << args);
}

int main()
{
    foo(1, 2, 3);
    (((cout << 1) << 2) << 3);
}
#include <iostream>
#include <vector>
using namespace std;

vector<int> v;

template<typename ... Types>
void foo(Types ... args)
{
    (v.push_back(args), ...);
    for (auto n : v)
        cout << n << endl;
}

int main()
{
    foo(1, 2, 3);
    (((cout << 1) << 2) << 3);
}
반응형

반응형

가변인자 템플릿(Variadic template)

  • C++11부터 지원되는 문법

  • 가변인자 템플릿(클래스 템플릿, 함수 템플릿)의 기본 모양

  • 가변인자 템플릿의 인자 "Types"는 여러개의 타입을 나타냄

  • 가변인자 함수 템플릿의 인자인 "args"안에는 여러개의 값이 들어있고 "Parameter Pack"이라고 함

#include <iostream>
using namespace std;

// 가변인자 클래스 템플릿.
template<typename ... Types> class tuplex
{

};

// 가변인자 함수 템플릿
template<typename ... Types>
void foo(Types ... args)
{

}

int main()
{
    // 가변인자 클래스 템플릿(타입 생략, 복수의 타입까지...)
    tuplex<> t0p;
    tuplex<int> t1;
    tuplex<int, char> t2;
    
    // 가변인자 함수 템플릿(인자 생략, 복수의 인자까지...)
    foo();
    foo(1);
    foo(1, 3.4, "A");
}

Parameter Pack

  • 가변인자 템플릿으로 전달받은 파라미터 집합을 Parameter Pack이라 함

  • sizeof...(args), sizeof...(Types) 함수를 이용해서 Parameter Pack 요소의 갯수를 얻어 올 수 있음

  • goo(args...); 받은 Parameter Pack을 다시 전달할때 요소 전체를 풀어쓰는 방법을 Pack Expansion이라 함

#include <iostream>
using namespace std;

// Pack expansion을 통해 Parameter Pack을 전달 받을 함수
void goo(int n, double d, const char* s)
{
    cout << "goo : " << n << ", " << d << ", " << s << endl;
    // 1, 3.4, "AAA"
}

// 가변인자 함수 템플릿
template<typename ... Types>
void foo(Types ... args)
{
    // args : Parameter Pack
    
    cout << sizeof...(args) << endl; // 3
    cout << sizeof...(Types) << endl; // 3

    //goo(args); // Error
    goo(args...); // Pack expansion
                  // goo( 1, 3.4, "AAA")
}

int main()
{
    //foo();
    //foo(1);
    foo(1, 3.4, "AAA"); 
        // Types : int, double, const char*
        // args : 1, 3.4, "AAA"

}

Pack Expansion

  • Parameter Pack을 사용하는 패턴에 "..." 붙이면 해당 패턴이 ',' 구분자로 확장되는 형태임

  • args이 1, 2, 3을 가지고 있을때

    • args... -> 1, 2, 3 

    • (--args)... -> 0, 1, 2

    • func(args)... -> func(1), func(2), func(3)

    • func(args...) -> func(1, 2, 3)

    • func2(func(args...)) -> func2(func(1, 2, 3))

    • func2(func(args)...)) -> func2(func(1), func(2), func(3))

  • Pack Expansion은 함수 호출 인자 또는 list 초기화를 사용한 표현식에서만 사용할 수 있음

    • 배열 이용

      • 컴파일러에 따라 빈값이 허용 안될 수 있으므로 첫번째 요소를 더미로 0으로 지정

      • 함수가 반환 타입이 void 경우 대비해서 대체 반환값 (x, 0) 패턴 사용

        • int x[] = { 0, (print(args), 0)... };

    • 초기화 리스트 이용

      • 빈값이 허용 됨으로 배열에 비해서 첫번째 요소 더미 0을 생략 할 수 있음

      • initializer_list<int> e = { (print(args), 0)... };

#include <iostream>
using namespace std;

void goo(int n, double d, const char* s)
{
    cout << "goo : " << n << " " << d << " " << s << endl;
}

int hoo(int a) { return -a; }

int print(int a)
{
    cout << a << ", ";
    return 0;
}

template<typename ... Types>
void foo(Types ... args)
{
    int x[] = { args... }; // Pack expansion
    for (auto n : x)
        cout << n << endl;

    //print(args); // Error(함수 호출 인자 or list 초기화가 아님)
    //print(args...); // Error(함수 호출 인자 or list 초기화가 아님)
    //print(args)...; // Error(함수 호출 인자 or list 초기화가 아님)

    // 배열 이용
    int ea[] = { 0, (print(args), 0)... };

    // 초기화 리스트 이용
    initializer_list<int> ei = { (print(args), 0)... };

}

int main()
{
    foo(1, 2, 3);
     // Types : int, int, int
     // args : 1, 2, 3
}

 

Expansion 예제

#include <iostream>
using namespace std;

template<typename ... Types> void foo(Types ... args)
{
    // Types : int, double

    int x[] = { args... };
    pair<Types...> p1; // pair<int, double> // OK
    tuple<Types...> t1; // tuple<int, double> // OK

    tuple<pair<Types...>> t2; // tuple<pair<int, double>> // OK
    tuple<pair<Types>...> t3; // tuple<pair<int>, pair<double>> // Error

    tuple<pair<int, Types>...> t4; // tuple<pair<int, int>, pair<int, double>> // OK

    pair<tuple<Types...>> p2; // pair<tuple<int, double>> // Error
    pair<tuple<Types>...> p3; // pair<tuple<int>, tuple<double>> // OK

}

int main()
{
    foo(1, 3.4); 
      // args : 1, 3.4
      // Types : int, double
}
반응형

+ Recent posts