ν…œν”Œλ¦Ώ

ν…œν”Œλ¦Ώ

1. ν…œν”Œλ¦Ώμ΄λž€?

ν…œν”Œλ¦Ώμ€ νƒ€μž…μ— 독립적인 μΌλ°˜ν™”λœ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” C++의 κ°•λ ₯ν•œ κΈ°λŠ₯μž…λ‹ˆλ‹€.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           ν…œν”Œλ¦Ώ (Template)                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β€’ νƒ€μž…μ„ λ§€κ°œλ³€μˆ˜λ‘œ λ°›λŠ” μ½”λ“œ                   β”‚
β”‚  β€’ 컴파일 νƒ€μž„μ— μ‹€μ œ νƒ€μž…μœΌλ‘œ λŒ€μ²΄               β”‚
β”‚  β€’ μ½”λ“œ μž¬μ‚¬μš©μ„± κ·ΉλŒ€ν™”                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  ν•¨μˆ˜ ν…œν”Œλ¦Ώ      β”‚  클래슀 ν…œν”Œλ¦Ώ    β”‚
β”‚  (Function)      β”‚  (Class)        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

μ™œ ν…œν”Œλ¦Ώμ΄ ν•„μš”ν•œκ°€?

// ν…œν”Œλ¦Ώ 없이 μ˜€λ²„λ‘œλ”©μœΌλ‘œ κ΅¬ν˜„
int max(int a, int b) { return (a > b) ? a : b; }
double max(double a, double b) { return (a > b) ? a : b; }
char max(char a, char b) { return (a > b) ? a : b; }
// ... λͺ¨λ“  νƒ€μž…λ§ˆλ‹€ 반볡 ν•„μš”

// ν…œν”Œλ¦ΏμœΌλ‘œ ν•œ λ²ˆμ— ν•΄κ²°
template<typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

2. ν•¨μˆ˜ ν…œν”Œλ¦Ώ

κΈ°λ³Έ 문법

#include <iostream>

// ν•¨μˆ˜ ν…œν”Œλ¦Ώ μ •μ˜
template<typename T>
T add(T a, T b) {
    return a + b;
}

// typename λŒ€μ‹  class도 μ‚¬μš© κ°€λŠ₯ (의미 동일)
template<class T>
T multiply(T a, T b) {
    return a * b;
}

int main() {
    // λͺ…μ‹œμ  νƒ€μž… μ§€μ •
    std::cout << add<int>(3, 5) << std::endl;        // 8
    std::cout << add<double>(3.5, 2.5) << std::endl; // 6

    // νƒ€μž… μΆ”λ‘  (μ»΄νŒŒμΌλŸ¬κ°€ μžλ™μœΌλ‘œ νƒ€μž… κ²°μ •)
    std::cout << add(10, 20) << std::endl;           // 30 (int)
    std::cout << add(1.5, 2.5) << std::endl;         // 4 (double)

    std::cout << multiply(4, 5) << std::endl;        // 20

    return 0;
}

μ—¬λŸ¬ νƒ€μž… λ§€κ°œλ³€μˆ˜

#include <iostream>
#include <string>

template<typename T, typename U>
void printPair(T first, U second) {
    std::cout << first << ", " << second << std::endl;
}

// λ°˜ν™˜ νƒ€μž…λ„ ν…œν”Œλ¦ΏμœΌλ‘œ
template<typename T, typename U>
auto addDifferent(T a, U b) -> decltype(a + b) {
    return a + b;
}

// C++14: κ°„λ‹¨ν•œ auto λ°˜ν™˜
template<typename T, typename U>
auto addSimple(T a, U b) {
    return a + b;
}

int main() {
    printPair(1, "Hello");         // 1, Hello
    printPair(3.14, 100);          // 3.14, 100
    printPair("Name", std::string("Alice"));  // Name, Alice

    std::cout << addDifferent(10, 3.5) << std::endl;  // 13.5 (double)
    std::cout << addSimple(5, 2.5) << std::endl;      // 7.5

    return 0;
}

λΉ„νƒ€μž… ν…œν”Œλ¦Ώ λ§€κ°œλ³€μˆ˜

#include <iostream>
#include <array>

// μ •μˆ˜κ°’μ„ ν…œν”Œλ¦Ώ λ§€κ°œλ³€μˆ˜λ‘œ
template<typename T, int Size>
class FixedArray {
private:
    T data[Size];
public:
    T& operator[](int index) { return data[index]; }
    const T& operator[](int index) const { return data[index]; }
    int size() const { return Size; }
};

// ν•¨μˆ˜μ—μ„œλ„ μ‚¬μš© κ°€λŠ₯
template<int N>
int factorial() {
    return N * factorial<N - 1>();
}

template<>
int factorial<0>() {
    return 1;
}

int main() {
    FixedArray<int, 5> arr;
    for (int i = 0; i < arr.size(); i++) {
        arr[i] = i * 10;
    }

    for (int i = 0; i < arr.size(); i++) {
        std::cout << arr[i] << " ";  // 0 10 20 30 40
    }
    std::cout << std::endl;

    // 컴파일 νƒ€μž„μ— 계산됨
    std::cout << "5! = " << factorial<5>() << std::endl;  // 120

    return 0;
}

3. 클래슀 ν…œν”Œλ¦Ώ

κΈ°λ³Έ 문법

#include <iostream>

template<typename T>
class Box {
private:
    T value;

public:
    Box(T v) : value(v) {}

    T getValue() const { return value; }
    void setValue(T v) { value = v; }

    void display() const {
        std::cout << "Box: " << value << std::endl;
    }
};

int main() {
    Box<int> intBox(42);
    intBox.display();  // Box: 42

    Box<double> doubleBox(3.14);
    doubleBox.display();  // Box: 3.14

    Box<std::string> stringBox("Hello");
    stringBox.display();  // Box: Hello

    return 0;
}

멀버 ν•¨μˆ˜ μ™ΈλΆ€ μ •μ˜

#include <iostream>

template<typename T>
class Calculator {
private:
    T value;

public:
    Calculator(T v);
    T add(T x);
    T subtract(T x);
    void display() const;
};

// 멀버 ν•¨μˆ˜ μ™ΈλΆ€ μ •μ˜ μ‹œ template μ„ μ–Έ ν•„μš”
template<typename T>
Calculator<T>::Calculator(T v) : value(v) {}

template<typename T>
T Calculator<T>::add(T x) {
    return value + x;
}

template<typename T>
T Calculator<T>::subtract(T x) {
    return value - x;
}

template<typename T>
void Calculator<T>::display() const {
    std::cout << "Value: " << value << std::endl;
}

int main() {
    Calculator<int> calc(10);
    std::cout << calc.add(5) << std::endl;      // 15
    std::cout << calc.subtract(3) << std::endl;  // 7
    calc.display();  // Value: 10

    return 0;
}

μ—¬λŸ¬ νƒ€μž… λ§€κ°œλ³€μˆ˜

#include <iostream>
#include <string>

template<typename K, typename V>
class Pair {
private:
    K key;
    V value;

public:
    Pair(K k, V v) : key(k), value(v) {}

    K getKey() const { return key; }
    V getValue() const { return value; }

    void display() const {
        std::cout << key << ": " << value << std::endl;
    }
};

int main() {
    Pair<std::string, int> age("Alice", 25);
    age.display();  // Alice: 25

    Pair<int, std::string> student(1001, "Bob");
    student.display();  // 1001: Bob

    Pair<std::string, double> price("Apple", 1.99);
    price.display();  // Apple: 1.99

    return 0;
}

κΈ°λ³Έ ν…œν”Œλ¦Ώ 인자

#include <iostream>
#include <vector>

template<typename T = int, int Size = 10>
class Array {
private:
    T data[Size];
    int count = 0;

public:
    void add(T value) {
        if (count < Size) {
            data[count++] = value;
        }
    }

    void display() const {
        for (int i = 0; i < count; i++) {
            std::cout << data[i] << " ";
        }
        std::cout << std::endl;
    }

    int capacity() const { return Size; }
};

int main() {
    Array<> arr1;  // int, 10 (κΈ°λ³Έκ°’)
    arr1.add(1);
    arr1.add(2);
    arr1.display();  // 1 2

    Array<double> arr2;  // double, 10
    arr2.add(1.5);
    arr2.add(2.5);
    arr2.display();  // 1.5 2.5

    Array<std::string, 5> arr3;  // string, 5
    arr3.add("Hello");
    arr3.add("World");
    arr3.display();  // Hello World

    return 0;
}

4. ν…œν”Œλ¦Ώ νŠΉμˆ˜ν™”

전체 νŠΉμˆ˜ν™” (Full Specialization)

#include <iostream>
#include <cstring>

// κΈ°λ³Έ ν…œν”Œλ¦Ώ
template<typename T>
class DataHolder {
private:
    T data;
public:
    DataHolder(T d) : data(d) {}
    void display() const {
        std::cout << "일반: " << data << std::endl;
    }
};

// char* νƒ€μž…μ— λŒ€ν•œ 전체 νŠΉμˆ˜ν™”
template<>
class DataHolder<char*> {
private:
    char* data;
public:
    DataHolder(const char* d) {
        data = new char[strlen(d) + 1];
        strcpy(data, d);
    }
    ~DataHolder() { delete[] data; }
    void display() const {
        std::cout << "char*: " << data << std::endl;
    }
};

// bool νƒ€μž…μ— λŒ€ν•œ 전체 νŠΉμˆ˜ν™”
template<>
class DataHolder<bool> {
private:
    bool data;
public:
    DataHolder(bool d) : data(d) {}
    void display() const {
        std::cout << "bool: " << (data ? "true" : "false") << std::endl;
    }
};

int main() {
    DataHolder<int> h1(42);
    h1.display();  // 일반: 42

    DataHolder<char*> h2("Hello");
    h2.display();  // char*: Hello

    DataHolder<bool> h3(true);
    h3.display();  // bool: true

    return 0;
}

λΆ€λΆ„ νŠΉμˆ˜ν™” (Partial Specialization)

#include <iostream>

// κΈ°λ³Έ ν…œν”Œλ¦Ώ
template<typename T, typename U>
class Pair {
public:
    void info() const {
        std::cout << "일반 Pair<T, U>" << std::endl;
    }
};

// 두 νƒ€μž…μ΄ 같을 λ•Œ λΆ€λΆ„ νŠΉμˆ˜ν™”
template<typename T>
class Pair<T, T> {
public:
    void info() const {
        std::cout << "같은 νƒ€μž… Pair<T, T>" << std::endl;
    }
};

// 두 λ²ˆμ§Έκ°€ int일 λ•Œ λΆ€λΆ„ νŠΉμˆ˜ν™”
template<typename T>
class Pair<T, int> {
public:
    void info() const {
        std::cout << "Pair<T, int>" << std::endl;
    }
};

// 포인터 νƒ€μž… λΆ€λΆ„ νŠΉμˆ˜ν™”
template<typename T, typename U>
class Pair<T*, U*> {
public:
    void info() const {
        std::cout << "포인터 Pair<T*, U*>" << std::endl;
    }
};

int main() {
    Pair<double, char> p1;
    p1.info();  // 일반 Pair<T, U>

    Pair<double, double> p2;
    p2.info();  // 같은 νƒ€μž… Pair<T, T>

    Pair<double, int> p3;
    p3.info();  // Pair<T, int>

    Pair<int*, double*> p4;
    p4.info();  // 포인터 Pair<T*, U*>

    return 0;
}

ν•¨μˆ˜ ν…œν”Œλ¦Ώ νŠΉμˆ˜ν™”

#include <iostream>
#include <cstring>

// κΈ°λ³Έ ν…œν”Œλ¦Ώ
template<typename T>
bool isEqual(T a, T b) {
    return a == b;
}

// char* νŠΉμˆ˜ν™”
template<>
bool isEqual<const char*>(const char* a, const char* b) {
    return strcmp(a, b) == 0;
}

int main() {
    std::cout << std::boolalpha;

    std::cout << isEqual(10, 10) << std::endl;           // true
    std::cout << isEqual(3.14, 3.14) << std::endl;       // true
    std::cout << isEqual("Hello", "Hello") << std::endl; // true (포인터 μ£Όμ†Œ 비ꡐ)

    const char* s1 = "Hello";
    const char* s2 = "Hello";
    std::cout << isEqual(s1, s2) << std::endl;           // true (λ¬Έμžμ—΄ λ‚΄μš© 비ꡐ)

    return 0;
}

5. κ°€λ³€ 인자 ν…œν”Œλ¦Ώ (Variadic Templates)

κΈ°λ³Έ 문법

#include <iostream>

// μž¬κ·€ μ’…λ£Œ 쑰건 (base case)
void print() {
    std::cout << std::endl;
}

// κ°€λ³€ 인자 ν…œν”Œλ¦Ώ
template<typename T, typename... Args>
void print(T first, Args... args) {
    std::cout << first;
    if (sizeof...(args) > 0) {
        std::cout << ", ";
    }
    print(args...);  // μž¬κ·€ 호좜
}

int main() {
    print(1, 2, 3);                    // 1, 2, 3
    print("Hello", 3.14, 42, 'A');     // Hello, 3.14, 42, A
    print("Name:", "Alice", "Age:", 25);  // Name:, Alice, Age:, 25

    return 0;
}

합계 계산

#include <iostream>

// μž¬κ·€ μ’…λ£Œ
template<typename T>
T sum(T value) {
    return value;
}

// κ°€λ³€ 인자
template<typename T, typename... Args>
T sum(T first, Args... args) {
    return first + sum(args...);
}

int main() {
    std::cout << sum(1, 2, 3, 4, 5) << std::endl;     // 15
    std::cout << sum(1.5, 2.5, 3.0) << std::endl;     // 7
    std::cout << sum(10) << std::endl;                 // 10

    return 0;
}

sizeof... μ—°μ‚°μž

#include <iostream>

template<typename... Args>
void countArgs(Args... args) {
    std::cout << "인자 개수: " << sizeof...(Args) << std::endl;
    std::cout << "인자 개수: " << sizeof...(args) << std::endl;  // 같은 κ²°κ³Ό
}

int main() {
    countArgs();                 // 인자 개수: 0
    countArgs(1);                // 인자 개수: 1
    countArgs(1, 2, 3);          // 인자 개수: 3
    countArgs("a", 1, 3.14, 'c'); // 인자 개수: 4

    return 0;
}

ν΄λ“œ ν‘œν˜„μ‹ (C++17)

#include <iostream>

// C++17 ν΄λ“œ ν‘œν˜„μ‹μœΌλ‘œ κ°„λ‹¨ν•˜κ²Œ
template<typename... Args>
auto sumFold(Args... args) {
    return (args + ...);  // 우츑 ν΄λ“œ
}

template<typename... Args>
void printFold(Args... args) {
    ((std::cout << args << " "), ...);  // 콀마 μ—°μ‚°μž ν΄λ“œ
    std::cout << std::endl;
}

template<typename... Args>
bool allTrue(Args... args) {
    return (args && ...);  // λͺ¨λ‘ true인지
}

template<typename... Args>
bool anyTrue(Args... args) {
    return (args || ...);  // ν•˜λ‚˜λΌλ„ true인지
}

int main() {
    std::cout << sumFold(1, 2, 3, 4, 5) << std::endl;  // 15

    printFold(1, "Hello", 3.14);  // 1 Hello 3.14

    std::cout << std::boolalpha;
    std::cout << allTrue(true, true, true) << std::endl;   // true
    std::cout << allTrue(true, false, true) << std::endl;  // false
    std::cout << anyTrue(false, false, true) << std::endl; // true

    return 0;
}

6. SFINAE

SFINAE (Substitution Failure Is Not An Error): ν…œν”Œλ¦Ώ 인자 λŒ€μ²΄ μ‹€νŒ¨λŠ” μ—λŸ¬κ°€ μ•„λ‹™λ‹ˆλ‹€.

κΈ°λ³Έ κ°œλ…

#include <iostream>
#include <type_traits>

// μ •μˆ˜ νƒ€μž…μΌ λ•Œλ§Œ ν™œμ„±ν™”
template<typename T>
typename std::enable_if<std::is_integral<T>::value, void>::type
process(T value) {
    std::cout << "μ •μˆ˜: " << value << std::endl;
}

// λΆ€λ™μ†Œμˆ˜μ  νƒ€μž…μΌ λ•Œλ§Œ ν™œμ„±ν™”
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, void>::type
process(T value) {
    std::cout << "μ‹€μˆ˜: " << value << std::endl;
}

int main() {
    process(42);      // μ •μˆ˜: 42
    process(3.14);    // μ‹€μˆ˜: 3.14
    // process("Hi"); // 컴파일 μ—λŸ¬ (λ‘˜ λ‹€ λ§€μΉ­ μ•ˆ 됨)

    return 0;
}

C++17 if constexpr

#include <iostream>
#include <type_traits>

template<typename T>
void process(T value) {
    if constexpr (std::is_integral_v<T>) {
        std::cout << "μ •μˆ˜: " << value * 2 << std::endl;
    } else if constexpr (std::is_floating_point_v<T>) {
        std::cout << "μ‹€μˆ˜: " << value / 2 << std::endl;
    } else {
        std::cout << "기타: " << value << std::endl;
    }
}

int main() {
    process(10);        // μ •μˆ˜: 20
    process(5.0);       // μ‹€μˆ˜: 2.5
    process("Hello");   // 기타: Hello

    return 0;
}

7. νƒ€μž… νŠΉμ„± (Type Traits)

κΈ°λ³Έ νƒ€μž… νŠΉμ„±

#include <iostream>
#include <type_traits>

int main() {
    std::cout << std::boolalpha;

    // νƒ€μž… 검사
    std::cout << "is_integral<int>: "
              << std::is_integral<int>::value << std::endl;  // true
    std::cout << "is_integral<double>: "
              << std::is_integral<double>::value << std::endl;  // false

    std::cout << "is_floating_point<double>: "
              << std::is_floating_point<double>::value << std::endl;  // true

    std::cout << "is_pointer<int*>: "
              << std::is_pointer<int*>::value << std::endl;  // true

    std::cout << "is_class<std::string>: "
              << std::is_class<std::string>::value << std::endl;  // true

    // νƒ€μž… λ³€ν™˜
    std::cout << "is_same<int, int>: "
              << std::is_same<int, int>::value << std::endl;  // true

    using NoRef = std::remove_reference<int&>::type;
    std::cout << "is_same<NoRef, int>: "
              << std::is_same<NoRef, int>::value << std::endl;  // true

    return 0;
}

쑰건뢀 νƒ€μž… 선택

#include <iostream>
#include <type_traits>

template<bool Condition, typename T, typename F>
struct MyConditional {
    using type = T;
};

template<typename T, typename F>
struct MyConditional<false, T, F> {
    using type = F;
};

int main() {
    // std::conditional μ‚¬μš©
    using Type1 = std::conditional<true, int, double>::type;
    using Type2 = std::conditional<false, int, double>::type;

    std::cout << std::boolalpha;
    std::cout << "Type1 is int: "
              << std::is_same<Type1, int>::value << std::endl;     // true
    std::cout << "Type2 is double: "
              << std::is_same<Type2, double>::value << std::endl;  // true

    // 크기에 λ”°λ₯Έ νƒ€μž… 선택
    using SmallType = std::conditional<(sizeof(int) > 4), long, int>::type;
    std::cout << "SmallType size: " << sizeof(SmallType) << std::endl;

    return 0;
}

8. Concepts (C++20)

κΈ°λ³Έ 문법

#include <iostream>
#include <concepts>

// concept μ •μ˜
template<typename T>
concept Numeric = std::is_arithmetic_v<T>;

template<typename T>
concept Addable = requires(T a, T b) {
    { a + b } -> std::same_as<T>;
};

// concept μ‚¬μš©
template<Numeric T>
T square(T x) {
    return x * x;
}

// requires 절 μ‚¬μš©
template<typename T>
requires Addable<T>
T add(T a, T b) {
    return a + b;
}

// μΆ•μ•½ν˜•
auto multiply(Numeric auto a, Numeric auto b) {
    return a * b;
}

int main() {
    std::cout << square(5) << std::endl;      // 25
    std::cout << square(3.5) << std::endl;    // 12.25
    // square("Hi");  // μ—λŸ¬: Numeric μ œμ•½ 뢈만쑱

    std::cout << add(10, 20) << std::endl;    // 30
    std::cout << multiply(3, 4) << std::endl; // 12

    return 0;
}

ν‘œμ€€ Concepts

#include <iostream>
#include <concepts>
#include <string>

// ν‘œμ€€ concept μ‚¬μš©
template<std::integral T>
void processInt(T value) {
    std::cout << "μ •μˆ˜: " << value << std::endl;
}

template<std::floating_point T>
void processFloat(T value) {
    std::cout << "μ‹€μˆ˜: " << value << std::endl;
}

template<std::convertible_to<std::string> T>
void processString(T value) {
    std::string s = value;
    std::cout << "λ¬Έμžμ—΄: " << s << std::endl;
}

int main() {
    processInt(42);
    processFloat(3.14);
    processString("Hello");

    return 0;
}

9. μ‹€μš©μ μΈ ν…œν”Œλ¦Ώ 예제

μ œλ„€λ¦­ μŠ€νƒ

#include <iostream>
#include <vector>
#include <stdexcept>

template<typename T>
class Stack {
private:
    std::vector<T> data;

public:
    void push(const T& value) {
        data.push_back(value);
    }

    T pop() {
        if (empty()) {
            throw std::runtime_error("μŠ€νƒμ΄ λΉ„μ–΄μžˆμŠ΅λ‹ˆλ‹€");
        }
        T value = data.back();
        data.pop_back();
        return value;
    }

    T& top() {
        if (empty()) {
            throw std::runtime_error("μŠ€νƒμ΄ λΉ„μ–΄μžˆμŠ΅λ‹ˆλ‹€");
        }
        return data.back();
    }

    bool empty() const { return data.empty(); }
    size_t size() const { return data.size(); }
};

int main() {
    Stack<int> intStack;
    intStack.push(1);
    intStack.push(2);
    intStack.push(3);

    while (!intStack.empty()) {
        std::cout << intStack.pop() << " ";  // 3 2 1
    }
    std::cout << std::endl;

    Stack<std::string> strStack;
    strStack.push("Hello");
    strStack.push("World");
    std::cout << strStack.top() << std::endl;  // World

    return 0;
}

νŒ©ν† λ¦¬ ν•¨μˆ˜

#include <iostream>
#include <memory>
#include <string>

// make ν•¨μˆ˜ ν…œν”Œλ¦Ώ
template<typename T, typename... Args>
std::unique_ptr<T> make(Args&&... args) {
    return std::make_unique<T>(std::forward<Args>(args)...);
}

class Person {
public:
    std::string name;
    int age;

    Person(std::string n, int a) : name(n), age(a) {
        std::cout << "Person 생성: " << name << std::endl;
    }

    void introduce() const {
        std::cout << name << ", " << age << "μ„Έ" << std::endl;
    }
};

int main() {
    auto p = make<Person>("Alice", 25);
    p->introduce();  // Alice, 25μ„Έ

    auto nums = make<std::vector<int>>(std::initializer_list<int>{1, 2, 3});
    for (int n : *nums) {
        std::cout << n << " ";  // 1 2 3
    }
    std::cout << std::endl;

    return 0;
}

νƒ€μž… μ•ˆμ „ printf

#include <iostream>
#include <sstream>
#include <string>

// μž¬κ·€ μ’…λ£Œ
void safePrint(std::ostream& os, const char* format) {
    while (*format) {
        if (*format == '%' && *(format + 1) != '%') {
            throw std::runtime_error("인자 λΆ€μ‘±");
        }
        if (*format == '%' && *(format + 1) == '%') {
            format++;  // %% κ±΄λ„ˆλ›°κΈ°
        }
        os << *format++;
    }
}

// κ°€λ³€ 인자 처리
template<typename T, typename... Args>
void safePrint(std::ostream& os, const char* format, T value, Args... args) {
    while (*format) {
        if (*format == '%') {
            if (*(format + 1) == '%') {
                os << '%';
                format += 2;
                continue;
            }
            os << value;
            safePrint(os, format + 1, args...);
            return;
        }
        os << *format++;
    }
    throw std::runtime_error("μΈμžκ°€ λ„ˆλ¬΄ 많음");
}

template<typename... Args>
std::string format(const char* fmt, Args... args) {
    std::ostringstream oss;
    safePrint(oss, fmt, args...);
    return oss.str();
}

int main() {
    std::cout << format("이름: %, λ‚˜μ΄: %μ„Έ", "Alice", 25) << std::endl;
    // 이름: Alice, λ‚˜μ΄: 25μ„Έ

    std::cout << format("% + % = %", 10, 20, 30) << std::endl;
    // 10 + 20 = 30

    return 0;
}

10. ν…œν”Œλ¦Ώ 컴파일 λͺ¨λΈ

헀더에 μ •μ˜ν•΄μ•Ό ν•˜λŠ” 이유

일반 ν•¨μˆ˜:                    ν…œν”Œλ¦Ώ:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ header.h    β”‚              β”‚ header.h    β”‚
β”‚ μ„ μ–Έλ§Œ      β”‚              β”‚ μ„ μ–Έ + μ •μ˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚                            β”‚
       β–Ό                            β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ source.cpp  β”‚              β”‚ (μ‚¬μš©μ²˜μ—μ„œ  β”‚
β”‚ μ •μ˜        β”‚              β”‚  μΈμŠ€ν„΄μŠ€ν™”) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

μ˜¬λ°”λ₯Έ ν…œν”Œλ¦Ώ ꡬ쑰

// mytemplate.h
#ifndef MYTEMPLATE_H
#define MYTEMPLATE_H

template<typename T>
class MyContainer {
private:
    T* data;
    size_t size;

public:
    MyContainer(size_t n);
    ~MyContainer();
    T& operator[](size_t index);
    size_t getSize() const;
};

// ν…œν”Œλ¦Ώ μ •μ˜λ„ 헀더에 포함
template<typename T>
MyContainer<T>::MyContainer(size_t n)
    : data(new T[n]), size(n) {}

template<typename T>
MyContainer<T>::~MyContainer() {
    delete[] data;
}

template<typename T>
T& MyContainer<T>::operator[](size_t index) {
    return data[index];
}

template<typename T>
size_t MyContainer<T>::getSize() const {
    return size;
}

#endif

λͺ…μ‹œμ  μΈμŠ€ν„΄μŠ€ν™” (선택적)

// mytemplate.cpp
#include "mytemplate.h"

// νŠΉμ • νƒ€μž…μ— λŒ€ν•΄ λͺ…μ‹œμ  μΈμŠ€ν„΄μŠ€ν™”
template class MyContainer<int>;
template class MyContainer<double>;
template class MyContainer<std::string>;

11. μš”μ•½

κ°œλ… μ„€λͺ…
ν•¨μˆ˜ ν…œν”Œλ¦Ώ νƒ€μž…μ— 독립적인 ν•¨μˆ˜
클래슀 ν…œν”Œλ¦Ώ νƒ€μž…μ— 독립적인 클래슀
ν…œν”Œλ¦Ώ νŠΉμˆ˜ν™” νŠΉμ • νƒ€μž…μ— λŒ€ν•œ νŠΉλ³„ κ΅¬ν˜„
λΆ€λΆ„ νŠΉμˆ˜ν™” 일뢀 쑰건에 λŒ€ν•œ νŠΉμˆ˜ν™”
κ°€λ³€ 인자 ν…œν”Œλ¦Ώ μž„μ˜ 개수의 인자 처리
SFINAE λŒ€μ²΄ μ‹€νŒ¨λŠ” μ—λŸ¬ μ•„λ‹˜
Concepts (C++20) ν…œν”Œλ¦Ώ μ œμ•½ 쑰건
λΉ„νƒ€μž… λ§€κ°œλ³€μˆ˜ 값을 ν…œν”Œλ¦Ώ 인자둜

12. μ—°μŠ΅ 문제

μ—°μŠ΅ 1: μ΅œμ†Œ/μ΅œλŒ€κ°’ ν•¨μˆ˜

μž„μ˜ 개수의 μΈμžμ—μ„œ μ΅œμ†Œκ°’κ³Ό μ΅œλŒ€κ°’μ„ λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜ ν…œν”Œλ¦Ώμ„ μž‘μ„±ν•˜μ„Έμš”.

μ—°μŠ΅ 2: μ œλ„€λ¦­ Queue

Stack 예제λ₯Ό μ°Έκ³ ν•˜μ—¬ Queue 클래슀 ν…œν”Œλ¦Ώμ„ μž‘μ„±ν•˜μ„Έμš”.

μ—°μŠ΅ 3: νƒ€μž…λ³„ 직렬화

λ‹€μ–‘ν•œ νƒ€μž…μ„ λ¬Έμžμ—΄λ‘œ λ³€ν™˜ν•˜λŠ” serialize ν•¨μˆ˜ ν…œν”Œλ¦Ώμ„ μž‘μ„±ν•˜μ„Έμš”. (κΈ°λ³Έ νƒ€μž…, μ»¨ν…Œμ΄λ„ˆ λ“±)


λ‹€μŒ 단계

13_Exceptions_and_File_IO.mdμ—μ„œ μ˜ˆμ™Έ μ²˜λ¦¬μ™€ 파일 I/Oλ₯Ό λ°°μ›Œλ΄…μ‹œλ‹€!

to navigate between lessons