C++ 헤더 온리 라이브러리 | "multiple definition" 에러 없이 만들기
이 글의 핵심
C++ 헤더 온리 라이브러리에 대한 실전 가이드입니다.
들어가며: “헤더에 함수를 정의했더니 링커 에러가 나요"
"헤더 온리 라이브러리는 어떻게 만드나요?”
C++에서 헤더에 함수를 정의하면 multiple definition 링커 에러가 발생합니다. 하지만 inline, template, constexpr을 사용하면 헤더 온리 라이브러리를 만들 수 있습니다.
// ❌ 헤더에 일반 함수 정의
// utils.h
void foo() { // ❌ multiple definition
std::cout << "foo\n";
}
// ✅ inline 사용
// utils.h
inline void foo() { // ✅ OK
std::cout << "foo\n";
}
이 글에서 다루는 것:
- 헤더 온리 라이브러리란?
- inline, template, constexpr
- ODR (One Definition Rule)
- 장단점과 사용 시기
목차
1. 헤더 온리 라이브러리란?
정의
헤더 온리 라이브러리는 .cpp 파일 없이 헤더 파일만으로 구성된 라이브러리입니다.
// 일반 라이브러리
// math.h
int add(int a, int b);
// math.cpp
int add(int a, int b) {
return a + b;
}
// 헤더 온리 라이브러리
// math.h
inline int add(int a, int b) {
return a + b;
}
// math.cpp 없음!
유명한 헤더 온리 라이브러리
- Eigen (선형대수)
- nlohmann/json (JSON 파싱)
- Catch2 (테스트 프레임워크)
- fmt (포맷팅, 헤더 온리 모드 지원)
2. inline 함수
inline 키워드
// utils.h
#ifndef UTILS_H
#define UTILS_H
#include <iostream>
inline void print(const std::string& msg) {
std::cout << msg << '\n';
}
inline int add(int a, int b) {
return a + b;
}
#endif
사용:
// main.cpp
#include "utils.h"
int main() {
print("Hello");
std::cout << add(1, 2) << '\n';
}
장점:
- multiple definition 에러 없음
- 컴파일러 최적화 가능
3. template
템플릿은 자동으로 헤더 온리
// math.h
#ifndef MATH_H
#define MATH_H
template <typename T>
T add(T a, T b) {
return a + b;
}
template <typename T>
class Vector {
T* data_;
size_t size_;
public:
Vector(size_t size) : data_(new T[size]), size_(size) {}
~Vector() {
delete[] data_;
}
T& operator {
return data_[i];
}
};
#endif
사용:
#include "math.h"
int main() {
std::cout << add(1, 2) << '\n';
std::cout << add(1.5, 2.5) << '\n';
Vector<int> vec(10);
vec[0] = 42;
}
4. constexpr
constexpr 함수
// math.h
#ifndef MATH_H
#define MATH_H
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
constexpr int power(int base, int exp) {
int result = 1;
for (int i = 0; i < exp; ++i) {
result *= base;
}
return result;
}
#endif
사용:
#include "math.h"
int main() {
constexpr int f5 = factorial(5); // 컴파일 타임 계산
std::cout << f5 << '\n'; // 120
constexpr int p = power(2, 10); // 1024
}
5. 장단점
장점
1. 사용 간편
// 헤더만 include
#include "mylib.h"
// 링크 설정 불필요
2. 컴파일러 최적화
// inline 함수는 인라인 전개 가능
inline int add(int a, int b) {
return a + b;
}
// 호출 오버헤드 없음
int x = add(1, 2); // → int x = 3;
3. 빌드 설정 불필요
- CMake 설정 간단
- 라이브러리 빌드 불필요
단점
1. 컴파일 시간 증가
// 헤더를 include하는 모든 파일에서 컴파일
#include "biglib.h" // 10000줄
// 100개 파일에서 include → 100번 컴파일
2. 헤더 변경 시 전체 재컴파일
// mylib.h 수정
inline void foo() {
// 변경
}
// mylib.h를 include하는 모든 파일 재컴파일
3. 바이너리 크기 증가
// inline 함수가 여러 번 인라인 전개
inline void bigFunction() {
// 100줄 코드
}
// 10곳에서 호출 → 1000줄 코드
실전 예시
헤더 온리 JSON 라이브러리
// json.h
#ifndef JSON_H
#define JSON_H
#include <string>
#include <map>
#include <vector>
#include <variant>
class Json {
using Value = std::variant<
std::nullptr_t,
bool,
int,
double,
std::string,
std::vector<Json>,
std::map<std::string, Json>
>;
Value value_;
public:
Json() : value_(nullptr) {}
Json(int v) : value_(v) {}
Json(const std::string& v) : value_(v) {}
template <typename T>
T get() const {
return std::get<T>(value_);
}
Json& operator {
auto& map = std::get<std::map<std::string, Json>>(value_);
return map[key];
}
};
#endif
사용:
#include "json.h"
int main() {
Json obj;
obj["name"] = "Alice";
obj["age"] = 30;
}
정리
헤더 온리 라이브러리 만들기
| 방법 | 사용 시기 |
|---|---|
| inline | 일반 함수 |
| template | 제네릭 코드 |
| constexpr | 컴파일 타임 계산 |
| class 정의 | 항상 가능 |
핵심 규칙
- inline 함수 (multiple definition 방지)
- template (자동으로 헤더 온리)
- constexpr (컴파일 타임 계산)
- 작은 라이브러리 (컴파일 시간 고려)
체크리스트
- inline 키워드를 사용하는가?
- 헤더 가드가 있는가?
- 컴파일 시간이 허용 가능한가?
- 바이너리 크기가 허용 가능한가?
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ 링커 에러 | multiple definition 해결
- C++ inline 함수 | 인라인 최적화
- C++ 템플릿 기초 | Template 가이드
- C++ constexpr | 컴파일 타임 계산
마치며
헤더 온리 라이브러리는 사용이 간편하지만, 컴파일 시간을 고려해야 합니다.
핵심 원칙:
- inline 함수 사용
- template 활용
- 작은 라이브러리
inline, template, constexpr로 헤더 온리 라이브러리를 만들어보세요.
다음 단계: 헤더 온리 라이브러리를 이해했다면, C++ 템플릿 가이드에서 더 깊이 배워보세요.
관련 글
- C++ 시리즈 전체 보기
- C++ Adapter Pattern 완벽 가이드 | 인터페이스 변환과 호환성
- C++ ADL |
- C++ Aggregate Initialization |