C++ Chrono Literals | "시간 리터럴" 가이드
이 글의 핵심
C++ Chrono Literals에 대한 실전 가이드입니다.
들어가며
C++14의 chrono 리터럴은 시간 값을 간결하고 읽기 쉽게 표현할 수 있게 해줍니다. std::chrono::milliseconds(500) 대신 500ms로 작성할 수 있습니다.
1. chrono 리터럴 기본
리터럴 종류
#include <iostream>
#include <chrono>
using namespace std::chrono_literals;
int main() {
// 시간 리터럴
auto ns = 100ns; // nanoseconds (나노초)
auto us = 100us; // microseconds (마이크로초)
auto ms = 100ms; // milliseconds (밀리초)
auto s = 5s; // seconds (초)
auto min = 10min; // minutes (분)
auto h = 2h; // hours (시간)
std::cout << "5초: " << s.count() << "s" << std::endl;
std::cout << "10분: " << min.count() << "min" << std::endl;
std::cout << "2시간: " << h.count() << "h" << std::endl;
}
리터럴 타입
| 리터럴 | 타입 | 설명 |
|---|---|---|
100ns | std::chrono::nanoseconds | 나노초 (10⁻⁹초) |
100us | std::chrono::microseconds | 마이크로초 (10⁻⁶초) |
100ms | std::chrono::milliseconds | 밀리초 (10⁻³초) |
5s | std::chrono::seconds | 초 |
10min | std::chrono::minutes | 분 |
2h | std::chrono::hours | 시간 |
핵심 개념:
- namespace:
std::chrono_literals필수 - 타입 안전: 컴파일 타임에 타입 체크
- 가독성: 코드가 명확하고 간결함
2. 시간 연산
기본 연산
#include <iostream>
#include <chrono>
using namespace std::chrono_literals;
int main() {
// 덧셈
auto total = 1h + 30min + 45s;
// 뺄셈
auto diff = 2h - 30min;
// 곱셈
auto doubled = 5s * 2;
// 나눗셈
auto half = 10min / 2;
// 비교
if (500ms < 1s) {
std::cout << "500ms는 1초보다 짧습니다" << std::endl;
}
}
단위 변환
#include <iostream>
#include <chrono>
using namespace std::chrono_literals;
int main() {
auto total = 1h + 30min + 45s;
// 초로 변환
auto totalSeconds = std::chrono::duration_cast<std::chrono::seconds>(total);
std::cout << "총 " << totalSeconds.count() << "초" << std::endl;
// 출력: 총 5445초
// 밀리초로 변환
auto totalMs = std::chrono::duration_cast<std::chrono::milliseconds>(total);
std::cout << "총 " << totalMs.count() << "ms" << std::endl;
// 출력: 총 5445000ms
// 분으로 변환 (소수점 버림)
auto totalMin = std::chrono::duration_cast<std::chrono::minutes>(total);
std::cout << "총 " << totalMin.count() << "분" << std::endl;
// 출력: 총 90분
}
3. 실전 예제
예제 1: sleep
#include <iostream>
#include <thread>
#include <chrono>
using namespace std::chrono_literals;
int main() {
std::cout << "시작" << std::endl;
// 기존 방식 (복잡)
// std::this_thread::sleep_for(std::chrono::seconds(2));
// 리터럴 사용 (간결)
std::this_thread::sleep_for(2s);
std::cout << "2초 후" << std::endl;
// 밀리초 단위
std::this_thread::sleep_for(500ms);
std::cout << "0.5초 후" << std::endl;
}
예제 2: 타임아웃
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
using namespace std::chrono_literals;
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void worker() {
std::this_thread::sleep_for(1s);
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one();
}
int main() {
std::thread t(worker);
std::unique_lock<std::mutex> lock(mtx);
// 500ms 대기
if (cv.wait_for(lock, 500ms, []{ return ready; })) {
std::cout << "준비 완료" << std::endl;
} else {
std::cout << "타임아웃 (500ms)" << std::endl;
}
// 2초 대기
if (cv.wait_for(lock, 2s, []{ return ready; })) {
std::cout << "준비 완료" << std::endl;
}
t.join();
}
예제 3: 타이머 클래스
#include <iostream>
#include <chrono>
#include <thread>
using namespace std::chrono_literals;
class Timer {
std::chrono::steady_clock::time_point start;
public:
Timer() : start(std::chrono::steady_clock::now()) {}
// 경과 시간 확인
template<typename Duration>
bool elapsed(Duration timeout) const {
auto now = std::chrono::steady_clock::now();
return now - start >= timeout;
}
// 경과 시간 반환
auto getElapsed() const {
auto now = std::chrono::steady_clock::now();
return now - start;
}
// 리셋
void reset() {
start = std::chrono::steady_clock::now();
}
};
int main() {
Timer timer;
int count = 0;
// 5초 동안 작업
while (!timer.elapsed(5s)) {
std::this_thread::sleep_for(100ms);
count++;
if (count % 10 == 0) {
auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(
timer.getElapsed()
);
std::cout << elapsed.count() << "초 경과..." << std::endl;
}
}
std::cout << "5초 경과, 총 " << count << "번 실행" << std::endl;
}
4. C++20 날짜 리터럴
날짜 리터럴 사용
#include <iostream>
#include <chrono>
using namespace std::chrono;
using namespace std::chrono_literals;
int main() {
// 날짜 리터럴 (C++20)
auto year = 2026y;
auto day = 29d;
// 날짜 생성
auto date = 2026y / March / 29d;
std::cout << date << std::endl;
// 2026-03-29
// 시간과 날짜 결합
auto datetime = sys_days{2026y / March / 29d} + 14h + 30min;
}
날짜 연산
#include <iostream>
#include <chrono>
using namespace std::chrono;
using namespace std::chrono_literals;
int main() {
auto today = 2026y / March / 29d;
// 날짜 덧셈
auto nextWeek = sys_days{today} + days{7};
auto nextMonth = sys_days{today} + months{1};
// 날짜 차이
auto date1 = sys_days{2026y / March / 1d};
auto date2 = sys_days{2026y / March / 29d};
auto diff = date2 - date1;
std::cout << "차이: " << diff.count() << "일" << std::endl;
// 차이: 28일
}
5. 자주 발생하는 문제
문제 1: namespace 누락
#include <chrono>
int main() {
// ❌ namespace 없음
// auto sec = 5s; // 컴파일 에러
// ✅ namespace 사용
using namespace std::chrono_literals;
auto sec = 5s;
// ✅ 또는 명시적 호출
auto sec2 = std::chrono_literals::operator""s(5);
}
에러 메시지:
error: unable to find numeric literal operator 'operator""s'
문제 2: 타입 추론
#include <chrono>
using namespace std::chrono_literals;
int main() {
// auto: 정확한 타입 추론
auto ms = 100ms; // std::chrono::milliseconds
// 명시적 타입
std::chrono::milliseconds ms2 = 100ms;
// 암묵적 변환
std::chrono::seconds sec = 1000ms; // 1초
// ❌ 정밀도 손실
// std::chrono::seconds sec2 = 1500ms; // 컴파일 에러 (1.5초)
// ✅ duration_cast 사용
auto sec3 = std::chrono::duration_cast<std::chrono::seconds>(1500ms);
std::cout << sec3.count() << "초" << std::endl; // 1초 (버림)
}
문제 3: 오버플로우
#include <chrono>
#include <iostream>
using namespace std::chrono_literals;
int main() {
// ❌ 큰 값 주의 (오버플로우 가능)
// auto days = 365 * 24h; // int 오버플로우
// ✅ 명시적 타입 사용
auto hours = std::chrono::hours(365 * 24);
// ✅ 또는 리터럴 먼저 사용
auto oneDay = 24h;
auto year = oneDay * 365;
auto totalHours = std::chrono::duration_cast<std::chrono::hours>(year);
std::cout << "1년: " << totalHours.count() << "시간" << std::endl;
}
문제 4: 혼합 연산
#include <chrono>
#include <iostream>
using namespace std::chrono_literals;
int main() {
// 다른 단위 연산 (자동 변환)
auto total = 1h + 30min + 45s;
// 가장 작은 단위로 자동 변환됨 (초)
std::cout << "타입: " << typeid(total).name() << std::endl;
// 명시적 변환
auto totalMs = std::chrono::duration_cast<std::chrono::milliseconds>(total);
std::cout << totalMs.count() << "ms" << std::endl;
// 비교 연산 (자동 변환)
if (500ms < 1s) {
std::cout << "500ms는 1초보다 짧습니다" << std::endl;
}
}
6. 가독성 향상 패턴
기존 방식 vs 리터럴
#include <thread>
#include <chrono>
using namespace std::chrono_literals;
int main() {
// ❌ 기존 방식 (읽기 어려움)
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::this_thread::sleep_for(std::chrono::seconds(2));
std::this_thread::sleep_for(std::chrono::minutes(5));
// ✅ 리터럴 사용 (간결하고 명확)
std::this_thread::sleep_for(500ms);
std::this_thread::sleep_for(2s);
std::this_thread::sleep_for(5min);
// ❌ 복잡한 계산
auto timeout = std::chrono::seconds(60 * 5);
// ✅ 명확한 의도
auto timeout2 = 5min;
// ✅ 복잡한 시간 표현
auto duration = 1h + 30min + 45s;
}
실전 활용
#include <iostream>
#include <chrono>
#include <thread>
using namespace std::chrono_literals;
// 재시도 로직
template<typename Func>
bool retryWithTimeout(Func func, std::chrono::milliseconds timeout) {
auto start = std::chrono::steady_clock::now();
while (true) {
if (func()) {
return true;
}
auto elapsed = std::chrono::steady_clock::now() - start;
if (elapsed >= timeout) {
return false;
}
std::this_thread::sleep_for(100ms);
}
}
int main() {
int attempt = 0;
bool success = retryWithTimeout([&]() {
attempt++;
std::cout << "시도 " << attempt << std::endl;
return attempt >= 3; // 3번째에 성공
}, 5s);
if (success) {
std::cout << "성공!" << std::endl;
} else {
std::cout << "타임아웃" << std::endl;
}
}
정리
핵심 요약
- 리터럴 종류:
ns,us,ms,s,min,h - namespace:
std::chrono_literals필수 - 연산: 덧셈, 뺄셈, 곱셈, 나눗셈, 비교
- 변환:
duration_cast로 단위 변환 - C++20: 날짜 리터럴 (
y,d)
chrono 리터럴 장단점
| 장점 | 단점 |
|---|---|
| 가독성 향상 | namespace 선언 필요 |
| 타입 안전 | C++14 이상 필요 |
| 간결한 코드 | 헤더에서 using 주의 |
| 컴파일 타임 체크 | 큰 값 오버플로우 주의 |
실전 팁
-
namespace 사용
- 함수 내부나 cpp 파일에서만
using namespace사용 - 헤더 파일에서는 명시적 호출 사용
- 함수 내부나 cpp 파일에서만
-
타입 변환
- 정밀도 손실 주의 (
1500ms→seconds) duration_cast로 명시적 변환
- 정밀도 손실 주의 (
-
성능
- 리터럴은 컴파일 타임에 처리됨
- 런타임 오버헤드 없음
다음 단계
- C++ Chrono 가이드
- C++ Duration
- C++ Calendar and Timezone
관련 글
- C++ 시리즈 전체 보기
- C++ Adapter Pattern 완벽 가이드 | 인터페이스 변환과 호환성
- C++ ADL |
- C++ Aggregate Initialization |