| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
Tags
- Prisma
- 트라이
- Strongly Connected Component
- LCA
- R 그래프
- Github
- localstorage
- Express.js
- 최소 공통 조상
- Spin Lock
- 자바스크립트
- 분리 집합
- map
- 강한 연결 요소
- 벨만-포드
- PROJECT
- SCC
- trie
- ccw 알고리즘
- Behavior Design Pattern
- DP
- JavaScript
- 비트필드를 이용한 dp
- Binary Lifting
- MongoDB
- 비트마스킹
- 2-SAT
- 그래프 탐색
- 이분 탐색
- 게임 서버 아키텍처
Archives
- Today
- Total
dh_0e
[C++] lambda, 타입 추론(auto), 일반화 함수(template), Singleton in C++ 본문
람다(lambda) 함수
- JavaScript(Node.js)의 화살표 함수(arrow function)와 비슷한 개념
- 익명 함수를 정의하고 간결하게 전달하는 함수 객체
- 내부 동작과 문법, this 캡처와 클로저 처리 방식에서 arrow function과의 차이점을 보임
C++
auto add = [](int a, int b) {
return a + b;
};
std::cout << add(3, 4); // 7
Node.js (JavaScript)
const add = (a, b) => {
return a + b;
};
console.log(add(3, 4)); // 7
C++ lambda vs JS arrow function
| 항목 | C++ 람다 | JS 화살표 함수 |
| 클로저(캡처) | [], [=], [&] 등으로 명시적으로 지정 | 자동으로 외부 스코프의 변수 접근 가능 |
| this 바인딩 | 명시적 캡처 필요 | 화살표 함수는 자신의 this를 가지지 않음, 외부 this를 캡처함 |
| 타입 추론 | C++14부터 auto 또는 템플릿 사용 가능 | JS는 동적 타입이므로 자유롭고 항상 가능 |
| 함수 객체 표현 | std::function, 함수 객체로 처리 가능 | 일급 객체 (Function)로 언제든지 전달 가능 |
| 리턴 타입 명시 | 필요 시 -> int 등 명시 가능 | 명시 X, 자동 추론됨 |
| 실행 환경 | 컴파일 시 (정적 타입) | 런타임 시 (동적 타입) |
캡처(Capture)
- 람다 함수 내부에서 바깥 변수(지역 변수 or 멤버 변수)에 접근하려면, 해당 변수를 캡처해야 함
- 전역 변수는 캡처 없이 사용 가능
// [캡처리스트](파라미터들) { 함수 본문 };
int x = 10;
auto f = [x]() { // x를 값으로 캡처
std::cout << x << std::endl;
};
| 캡처 | 방식 문법 예 | 의미 |
| 값으로 캡처 | [x] | 변수 x의 값을 복사해서 사용 (람다 생성 시점) |
| 참조로 캡처 | [&x] | 변수 x에 대한 참조를 저장 (원본 직접 수정 가능) |
| 모든 변수 값으로 | [=] | 람다 밖에서 사용한 모든 변수를 값으로 캡처 |
| 모든 변수 참조로 | [&] | 람다 밖에서 사용한 모든 변수를 참조로 캡처 |
| 혼합 | [=, &y] | 기본은 값 캡처, y만 참조로 캡처 |
| this 캡처 | [this] | 람다가 선언된 클래스의 멤버에 접근 가능 |
| 객체 전체 캡처 | [=, this] | 멤버 함수 내부에서 this와 외부 지역 변수 동시 캡처 |
ex) 캡처 리스트 혼합
int a = 1, b = 2, c = 3;
auto mixed = [=, &b, &c]() {
// a는 값 복사
// b, c는 참조
std::cout << a << " " << b << " " << c << std::endl;
};
ex) 클래스 멤버와 [this]
class MyClass {
public:
int data = 42;
void run() {
auto f = [this]() {
std::cout << data << std::endl; // this->data
};
f();
}
};
- 값 캡처는 복사본이기 때문에 람다 내에서 변경해도 원본 변수는 바뀌지 않음
- 참조 캡처는 람다 외부에서 변경된 값이 반영됨
- 기본 캡처 [=] 또는 [&]를 사용할 때도 어떤 변수가 캡처되는지 정확히 파악해야 안전함
mutable, constexpr, noexcept
auto f = [x]() mutable noexcept -> int {
x += 1;
return x;
};
| 키워드 | 의미 |
| mutable | 캡처한 변수를 내부에서 수정 가능 |
| constexpr | 컴파일 타임 실행 보장 |
| noexcept | 예외를 발생시키지 않음을 명시 |
| -> T | 반환 타입 명시 |
template, auto
template: 일반화 프로그래밍
template <typename T>
T add(T a, T b) {
return a + b;
}
- 다양한 타입에 대해 코드 재사용 가능
- 컴파일 시 타입별로 인스턴스화됨
auto: 타입 추론
auto x = 10; // int로 추론
auto result = add(1, 2); // 반환값 타입 추론
- 변수, 반복자, 람다 매개변수 등에 쓰이는 타입 생략 도구
template vs auto
| 항목 | template | auto |
| 사용 목적 | 일반화된 코드 정의 | 타입 추론을 편하게 |
| 정의 위치 | 함수/클래스 정의 시 사용 | 변수 선언 시 사용 |
| 사용 예 | 여러 타입에 대해 하나의 코드 작성 | 타입을 몰라도 변수 선언 가능 |
| 타입 유추 시점 | 함수 호출 시점 | 컴파일러가 변수 선언 시점에 추론 |
C++11: decltype
- 표현식 기반 타입 추
int a = 3;
decltype(a) b = 5; // b는 int
- decltype(a + 0.5)는 double이 됨
C++14: 함수에서 auto 반환, generic lambda
auto 함수 반환 타입
auto add(int a, int b) {
return a + b; // 컴파일러가 반환 타입 추론
}
Generic Lambda
auto g = [](auto a, auto b) {
return a + b;
};
C++17: 구조화 바인딩과 if constexpr
구조화 바인딩(structured bindings)
tuple<int, string> t = {1, "hello"};
auto [id, name] = t;
if constexpr (템플릿 내부에서 조건 분기)
template<typename T>
void printType(T x) {
if constexpr (is_integral<T>::value) {
cout << "정수형입니다" << endl;
} else {
cout << "정수형이 아닙니다" << endl;
}
}
C++20: auto 파라미터 + Concepts로 템플릿 대체
auto 함수 파라미터
auto multiply(auto a, auto b) {
return a * b;
}
- 이는 사실 아래 코드의 축약형임:
template<typename T, typename U>
auto multiply(T a, U b) {
return a * b;
}
Concepts로 타입 제약 걸기
#include <concepts>
auto add(std::integral auto a, std::integral auto b) {
return a + b;
}
- std::integral → 정수형(int, long, short 등)만 허용
- add(1.2, 3.4) 하면 컴파일 에러 발생
- 코드 안정성과 명시성 증가
사용자 정의 Concept
template<typename T>
concept Addable = requires(T a, T b) {
a + b;
};
template<Addable T>
T add(T a, T b) {
return a + b;
}
- requires() 안에서 해당 연산이 가능한지 검사
- Addable이라는 이름의 concept에 정의
좀 더 복잡한 조건도 가능:
template<typename T>
concept StringLike = requires(T a) {
{ a.length() } -> std::convertible_to<size_t>;
{ a[0] } -> std::same_as<char>;
};
자주 사용하는 표준 concept
| Concept | 의미 |
| std::integral | 정수형 타입 (int, long 등) |
| std::floating_point | 부동소수 타입 (float, double) |
| std::same_as<T> | T와 같은 타입인지 |
| std::convertible_to<T> | T로 암시적 변환 가능한지 |
| std::default_initializable | 기본 생성 가능한지 |
| std::copyable, std::movable | 복사/이동 가능한지 |
Lambda + Concept (C++20)
auto intAdd = [](std::integral auto a, std::integral auto b) {
return a + b;
};
or
auto addOnlyStrings = [](const auto& a, const auto& b)
requires (std::same_as<decltype(a), string> && std::same_as<decltype(b), string>)
{
return a + b;
};
addOnlyStrings("hi"s, " there"s); // OK
addOnlyStrings(1, 2); // ❌ 컴파일 에러
| 기능 | C++98~03 | C++11 | C++14 | C++17 | C++20 |
| 타입 추론 | 없음 | auto | auto 반환 | if constexpr, structured bindings | auto 파라미터, concepts |
| 일반화 함수 | template<typename T> | - | auto 반환만 가능 | - | auto만으로 템플릿처럼 사용 가능 |
| 타입 제약 | enable_if, SFINAE | decltype, is_same | constexpr 조건 분기 | if constexpr | concepts |
Singleton in C++
- 클래스의 인스턴스를 하나만 만들 수 있도록 제한
- 어디서든 그 인스턴스에 전역적으로 접근 가능하게 함
- 생성자를 private으로 막고, 정적(static) 메서드를 통해 인스턴스를 생성 및 반환
구조
class Singleton {
private:
static Singleton* instance;
Singleton() {} // 생성자 private
public:
static Singleton* getInstance() {
if (!instance)
instance = new Singleton();
return instance;
}
void doSomething() {
cout << "Doing something!" << endl;
}
};
// 정적 멤버 초기화
Singleton* Singleton::instance = nullptr;
사용
int main() {
Singleton* s1 = Singleton::getInstance();
Singleton* s2 = Singleton::getInstance();
s1->doSomething();
cout << (s1 == s2) << endl; // 1 (true)
}
C++11 이후 안전한 싱글턴 (static local 방식)
class Singleton {
private:
Singleton() {}
public:
static Singleton& getInstance() {
static Singleton instance; // C++11 이상: thread-safe 보장
return instance;
}
void sayHello() {
cout << "Hello Singleton!" << endl;
}
// 복사, 대입 금지
Singleton(const Singleton&) = delete;
void operator=(const Singleton&) = delete;
};
int main() {
Singleton& s1 = Singleton::getInstance();
Singleton& s2 = Singleton::getInstance();
s1.sayHello();
cout << std::boolalpha << (&s1 == &s2) << endl; // true
}
'C++' 카테고리의 다른 글
| [C++] memset, isupper/islower (0) | 2025.02.13 |
|---|---|
| [C++] stringstream (2) | 2024.07.22 |
| [C++] max_element, min_element, find (0) | 2024.07.12 |
| [C++] map, unordered_map (0) | 2024.07.03 |
| [C++] 행렬 곱셈(matrix multiplication) 정의와 그 이유, 구현 (0) | 2024.06.25 |