C++ 템플릿 에러 메시지 해석 | "수백 줄 에러" 5분 만에 읽는 법
이 글의 핵심
C++ 템플릿 에러 메시지 해석에 대한 실전 가이드입니다.
들어가며: “error: 템플릿 에러 300줄… 뭐가 문제죠?"
"std::sort에 잘못된 타입을 넣었더니 에러가 200줄이 나왔어요”
C++ 템플릿을 사용하다 보면 수백 줄의 에러 메시지를 마주하게 됩니다. 특히 STL 알고리즘, 스마트 포인터, 컨테이너를 잘못 사용하면 템플릿 인스턴스화 체인이 길어져 에러 메시지가 폭발적으로 늘어납니다.
std::vector<std::unique_ptr<int>> vec;
std::sort(vec.begin(), vec.end()); // ❌ unique_ptr는 복사 불가
// 에러 메시지: 200줄 이상...
// error: no matching function for call to 'std::sort'
// note: candidate template ignored: requirement '__is_copy_constructible' was not satisfied
// ... (중략 150줄) ...
이 글에서 다루는 것:
- 템플릿 에러 메시지 구조 이해하기
- 핵심 정보만 빠르게 찾는 법 (첫 줄, 마지막 줄, required from)
- 자주 나오는 에러 패턴 10가지와 해결법
- 컴파일러별 에러 메시지 차이 (GCC, Clang, MSVC)
- Concepts로 에러 메시지 단축하기
- 실전 디버깅 전략
목차
1. 템플릿 에러 메시지 구조
전형적인 템플릿 에러 구조
[1] main.cpp:10:5: error: no matching function for call to 'std::sort'
std::sort(vec.begin(), vec.end());
^~~~~~~~~
[2] /usr/include/c++/11/bits/stl_algo.h:4842:5: note: candidate template ignored
sort(_RandomAccessIterator __first, _RandomAccessIterator __last)
^
/usr/include/c++/11/bits/stl_algo.h:4842:5: note: because
'std::unique_ptr<int>' does not satisfy '__is_copy_constructible'
[3] main.cpp:10:5: note: in instantiation of function template specialization
'std::sort<std::vector<std::unique_ptr<int>>::iterator>' requested here
std::sort(vec.begin(), vec.end());
^
[4] /usr/include/c++/11/bits/move.h:195:7: note: required from here
... (중략 100줄) ...
주의사항: 중간 bits/stl_*.h 수백 줄은 “왜 실패했는지”가 아니라 “어떤 템플릿이 전개되었는지”라서, 첫 error와 because 줄만 먼저 읽으면 됩니다.
구조 분석:
- [1] 실제 에러 위치: 내 코드의 어느 줄에서 에러가 발생했는지
- [2] 에러 원인: 왜 실패했는지 (타입 제약 위반, 인자 불일치 등)
- [3] 템플릿 인스턴스화 경로: 어떤 템플릿이 인스턴스화되었는지
- [4] 내부 구현 스택: STL 내부에서 어떤 경로로 에러가 전파되었는지
핵심: 중간 100줄은 무시해도 됩니다
중요한 정보:
- 첫 줄: 내 코드의 에러 위치
- “note: because” 부분: 실제 원인
- “candidate” 목록: 왜 각 오버로드가 실패했는지
무시해도 되는 부분:
- “required from here” 반복 (템플릿 인스턴스화 스택)
- STL 내부 구현 경로 (
bits/stl_*.h)
2. 에러 메시지 읽는 순서
1단계: 맨 위 첫 줄 확인
main.cpp:10:5: error: no matching function for call to 'std::sort'
^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
에러 타입 무엇이 문제인지
핵심 정보:
- 파일:줄:열:
main.cpp:10:5- 내 코드의 정확한 위치 - 에러 타입:
no matching function,ambiguous call,substitution failure - 함수 이름:
std::sort- 어떤 템플릿 함수가 문제인지
2단계: “note: because” 찾기
note: because 'std::unique_ptr<int>' does not satisfy '__is_copy_constructible'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
실제 원인
이 줄이 가장 중요합니다. 템플릿이 왜 인스턴스화에 실패했는지 알려줍니다.
3단계: candidate 목록 확인
note: candidate template ignored: requirement 'is_copy_constructible_v<T>' was not satisfied
note: candidate function not viable: requires 3 arguments, but 2 were provided
note: candidate function template not viable: 'this' argument has type 'const MyClass', but method is not marked const
각 후보가 왜 실패했는지 나와 있습니다. 가장 가까운 후보를 찾아 수정하면 됩니다.
4단계: 중간 스택 무시
note: in instantiation of function template specialization 'std::sort<...>' requested here
note: required from 'std::__sort<...>'
note: required from 'std::__introsort_loop<...>'
... (반복 100줄) ...
이 부분은 건너뛰세요. 템플릿이 어떻게 인스턴스화되었는지 보여주지만, 에러 원인과는 무관합니다.
3. 자주 나오는 에러 패턴 10가지
패턴 1: no matching function for call
가장 흔한 에러: 함수 오버로드를 찾지 못함.
// ❌ 에러 코드
std::vector<std::unique_ptr<int>> vec;
std::sort(vec.begin(), vec.end());
// error: no matching function for call to 'std::sort'
// note: candidate template ignored:
// 'std::unique_ptr<int>' does not satisfy '__is_copy_constructible'
원인: std::sort는 원소를 복사해야 하는데, unique_ptr는 복사 불가입니다.
해결법:
// ✅ 해결 1: 이동 가능한 타입으로 변경
std::vector<std::shared_ptr<int>> vec;
std::sort(vec.begin(), vec.end());
// ✅ 해결 2: 포인터로 정렬
std::vector<std::unique_ptr<int>> vec;
std::sort(vec.begin(), vec.end(),
{ return *a < *b; });
패턴 2: ambiguous call to overloaded function
에러: 여러 오버로드가 동일하게 매칭됨.
// ❌ 에러 코드
void print(int x) { std::cout << "int: " << x << '\n'; }
void print(double x) { std::cout << "double: " << x << '\n'; }
print(3.14f); // float → int? double?
// error: call to 'print' is ambiguous
// note: candidate function
// note: candidate function
원인: float가 int와 double 둘 다로 변환 가능합니다.
해결법:
// ✅ 해결 1: 명시적 캐스팅
print(static_cast<double>(3.14f));
// ✅ 해결 2: float 오버로드 추가
void print(float x) { std::cout << "float: " << x << '\n'; }
패턴 3: no type named ‘type’ in struct
에러: 타입 트레이트가 실패함.
// ❌ 에러 코드
template <typename T>
typename T::value_type getFirst(const T& container) {
return container[0];
}
int arr[5] = {1, 2, 3, 4, 5};
auto x = getFirst(arr); // int[]에는 value_type이 없음
// error: no type named 'value_type' in 'int[5]'
해결법:
// ✅ 해결 1: std::iterator_traits 사용
template <typename T>
auto getFirst(const T& container)
-> typename std::iterator_traits<decltype(std::begin(container))>::value_type {
return *std::begin(container);
}
// ✅ 해결 2: C++20 Concepts
template <typename T>
requires requires(T t) { t[0]; }
auto getFirst(const T& container) {
return container[0];
}
패턴 4: static assertion failed
에러: static_assert가 실패함.
// ❌ 에러 코드
template <typename T>
void process(T value) {
static_assert(std::is_integral_v<T>, "T must be integral type");
// ...
}
process(3.14); // double은 integral이 아님
// error: static assertion failed: T must be integral type
해결법:
// ✅ 올바른 타입 전달
process(42);
// ✅ 또는 제약 완화
template <typename T>
requires std::is_arithmetic_v<T> // integral 또는 floating_point
void process(T value) {
// ...
}
패턴 5: cannot convert from ‘X’ to ‘Y’
에러: 템플릿 인자 타입 불일치.
// ❌ 에러 코드
template <typename T>
void print(const std::vector<T>& vec) {
for (const T& x : vec) {
std::cout << x << '\n';
}
}
std::vector<int> vec = {1, 2, 3};
print<double>(vec); // vector<int>를 vector<double>로 취급
// error: cannot convert from 'const std::vector<int>' to 'const std::vector<double>&'
해결법:
// ✅ 타입 추론 사용
print(vec); // T = int로 자동 추론
// ✅ 또는 올바른 타입 명시
print<int>(vec);
패턴 6: incomplete type is not allowed
에러: 전방 선언만 있고 정의가 없음.
// ❌ 에러 코드
class MyClass; // 전방 선언만
std::vector<MyClass> vec; // vector는 완전한 타입 필요
// error: incomplete type 'MyClass' is not allowed
원인: std::vector는 원소의 크기를 알아야 하므로 완전한 타입이 필요합니다.
해결법:
// ✅ 해결 1: 정의 포함
#include "MyClass.h"
std::vector<MyClass> vec;
// ✅ 해결 2: 포인터 사용 (전방 선언만으로 가능)
std::vector<MyClass*> vec;
// ✅ 해결 3: unique_ptr 사용
std::vector<std::unique_ptr<MyClass>> vec;
패턴 7: template argument deduction failed
에러: 템플릿 인자를 추론할 수 없음.
// ❌ 에러 코드
template <typename T>
void process(T value, T other) {
// ...
}
process(42, 3.14); // T = int? double?
// error: no matching function for call to 'process'
// note: candidate template ignored: deduced conflicting types for parameter 'T'
해결법:
// ✅ 해결 1: 타입 명시
process<double>(42, 3.14);
// ✅ 해결 2: 두 개의 템플릿 인자
template <typename T1, typename T2>
void process(T1 value, T2 other) {
// ...
}
// ✅ 해결 3: 공통 타입 사용
template <typename T1, typename T2>
void process(T1 value, T2 other) {
using Common = std::common_type_t<T1, T2>;
Common a = value;
Common b = other;
// ...
}
패턴 8: invalid operands to binary expression
에러: 연산자를 사용할 수 없는 타입.
// ❌ 에러 코드
template <typename T>
T add(T a, T b) {
return a + b;
}
struct MyClass {};
MyClass obj1, obj2;
auto result = add(obj1, obj2); // MyClass에 operator+ 없음
// error: invalid operands to binary expression ('MyClass' and 'MyClass')
해결법:
// ✅ 해결 1: operator+ 정의
struct MyClass {
int value;
MyClass operator+(const MyClass& other) const {
return {value + other.value};
}
};
// ✅ 해결 2: Concepts로 제약
template <typename T>
requires requires(T a, T b) { a + b; }
T add(T a, T b) {
return a + b;
}
패턴 9: member access into incomplete type
에러: 전방 선언된 타입의 멤버 접근.
// ❌ 에러 코드
class MyClass;
template <typename T>
void print(const T& obj) {
std::cout << obj.value << '\n'; // MyClass 정의 필요
}
MyClass obj;
print(obj);
// error: member access into incomplete type 'MyClass'
해결법:
// ✅ 정의 포함
#include "MyClass.h"
template <typename T>
void print(const T& obj) {
std::cout << obj.value << '\n';
}
패턴 10: too many template arguments
에러: 템플릿 인자 개수 불일치.
// ❌ 에러 코드
std::vector<int, std::allocator<int>, int> vec; // 인자 3개
// error: too many template arguments for class template 'vector'
해결법:
// ✅ 올바른 인자 개수
std::vector<int> vec; // 기본 할당자 사용
std::vector<int, std::allocator<int>> vec2; // 명시적 할당자
4. 컴파일러별 에러 메시지 비교
동일한 에러, 다른 메시지
// 에러 코드
template <typename T>
void print(T value) {
std::cout << value.name << '\n'; // int에는 name 없음
}
print(42);
GCC 에러 메시지
main.cpp: In instantiation of 'void print(T) [with T = int]':
main.cpp:10:5: required from here
main.cpp:5:23: error: request for member 'name' in 'value', which is of non-class type 'int'
5 | std::cout << value.name << '\n';
| ^~~~
특징: 중간 정도 길이, “required from here”로 호출 경로 표시.
Clang 에러 메시지
main.cpp:5:23: error: member reference base type 'int' is not a structure or union
std::cout << value.name << '\n';
~~~~~ ^
main.cpp:10:5: note: in instantiation of function template specialization 'print<int>' requested here
print(42);
^
특징: 가장 읽기 쉬움. ^ 기호로 정확한 위치 표시, 간결한 메시지.
MSVC 에러 메시지
main.cpp(5): error C2228: left of '.name' must have class/struct/union
main.cpp(5): note: type is 'int'
main.cpp(10): note: see reference to function template instantiation 'void print<int>(T)' being compiled
with
[
T=int
]
특징: 가장 장황함. 템플릿 인자를 별도 블록으로 표시.
권장: Clang을 보조 컴파일러로 사용
# 주 컴파일러가 GCC/MSVC여도, 에러 확인용으로 Clang 사용
clang++ -std=c++17 -fsyntax-only main.cpp
# 또는 Compiler Explorer (godbolt.org) 사용
5. C++20 Concepts로 에러 단축
Before: 템플릿 에러 (50줄)
// C++17 이전
template <typename T>
void process(const std::vector<T>& vec) {
std::sort(vec.begin(), vec.end()); // const vector는 정렬 불가
}
// error: no matching function for call to 'std::sort'
// note: candidate template ignored: ...
// ... (중략 40줄) ...
After: Concepts 에러 (5줄)
// C++20
template <typename T>
requires std::sortable<typename std::vector<T>::iterator>
void process(std::vector<T>& vec) { // const 제거
std::sort(vec.begin(), vec.end());
}
// error: constraints not satisfied for 'process<int>'
// note: because 'std::vector<int>::const_iterator' does not satisfy 'sortable'
개선점: 에러 메시지가 10배 이상 짧아지고, 원인이 명확합니다.
Concepts 적용 예제
#include <concepts>
#include <vector>
#include <algorithm>
// 제약 조건 명시
template <typename T>
requires std::integral<T>
T add(T a, T b) {
return a + b;
}
// 사용
add(1, 2); // ✅ OK
add(1.5, 2.5); // ❌ error: constraints not satisfied
// note: because 'double' does not satisfy 'integral'
에러 메시지가 한 줄로 끝납니다!
6. 디버깅 전략
전략 1: 이진 탐색으로 원인 좁히기
// 복잡한 템플릿 코드
template <typename T>
void complex(const T& value) {
auto result = transform(value);
process(result);
output(result);
}
// 에러가 나면: 각 단계를 분리해서 테스트
template <typename T>
void complex(const T& value) {
auto result = transform(value);
// 여기까지 컴파일되나? → 주석 처리하며 확인
// process(result);
// output(result);
}
전략 2: 타입 출력으로 확인
// 타입이 뭔지 모를 때
template <typename T>
void debug_type(T value) {
// 컴파일 에러로 타입 출력
typename T::this_will_fail;
// 또는 C++20
static_assert(false, typeid(T).name());
}
// 실행 시 타입 확인
#include <typeinfo>
std::cout << typeid(value).name() << '\n';
전략 3: 단순화된 테스트 케이스
// 복잡한 코드에서 에러가 나면
// 최소 재현 코드(Minimal Reproducible Example) 작성
// Before: 복잡한 코드
template <typename T, typename U, typename V>
auto complex_function(T a, U b, V c) -> decltype(a + b * c) {
// 100줄 로직
}
// After: 최소 재현
template <typename T>
auto simple(T a, T b) -> decltype(a + b) {
return a + b;
}
// 이것만 테스트해서 에러 원인 파악
전략 4: 컴파일러 옵션 활용
# GCC: 템플릿 인스턴스화 깊이 제한
g++ -ftemplate-backtrace-limit=5 main.cpp
# Clang: 에러 메시지 색상 강조
clang++ -fcolor-diagnostics main.cpp
# MSVC: 에러 메시지 간소화
cl /diagnostics:caret main.cpp
전략 5: Concepts로 사전 검증
// 템플릿 사용 전에 제약 확인
template <typename T>
concept Addable = requires(T a, T b) {
{ a + b } -> std::convertible_to<T>;
};
template <Addable T>
T add(T a, T b) {
return a + b;
}
// 잘못된 타입은 즉시 에러
struct NoAdd {};
add(NoAdd{}, NoAdd{}); // error: constraints not satisfied
실전 사례 분석
사례 1: STL 알고리즘 타입 불일치
에러 코드:
std::vector<std::string> names = {"Alice", "Bob", "Charlie"};
std::vector<int> ids = {1, 2, 3};
// ❌ 타입 불일치
std::transform(names.begin(), names.end(), ids.begin(), ids.begin(),
{
return name + std::to_string(id);
});
// error: no matching function for call to 'std::transform'
// note: candidate template ignored: deduced conflicting types for parameter 'OutputIterator'
문제: std::transform의 출력 반복자가 ids.begin()인데, 결과 타입은 std::string입니다.
해결:
// ✅ 올바른 코드
std::vector<std::string> results;
std::transform(names.begin(), names.end(), ids.begin(),
std::back_inserter(results),
{
return name + std::to_string(id);
});
사례 2: 중첩 템플릿 타입 추론 실패
에러 코드:
// ❌ 에러 코드
template <typename Container>
void printFirst(const Container& c) {
typename Container::value_type::iterator it; // 중첩 타입
// ...
}
std::vector<int> vec = {1, 2, 3};
printFirst(vec);
// error: no type named 'iterator' in 'int'
문제: int에는 iterator가 없습니다.
해결:
// ✅ 올바른 코드
template <typename Container>
void printFirst(const Container& c) {
typename Container::iterator it = c.begin();
// ...
}
사례 3: SFINAE 실패
에러 코드:
// ❌ 에러 코드
template <typename T>
auto getSize(const T& container) -> decltype(container.size()) {
return container.size();
}
int arr[5];
auto s = getSize(arr); // 배열에는 size() 없음
// error: no matching function for call to 'getSize'
// note: candidate template ignored: substitution failure
해결:
// ✅ 해결 1: std::size 사용 (C++17)
#include <iterator>
auto s = std::size(arr); // 배열도 지원
// ✅ 해결 2: 오버로드 추가
template <typename T, size_t N>
size_t getSize(const T (&arr)[N]) {
return N;
}
에러 메시지 패턴 인식
”note: candidate” 읽는 법
note: candidate function template not viable: requires 2 arguments, but 1 was provided
^^^^^^^^ ^^^^^^^^ ^^^^^^^^ ^^^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
후보 함수 템플릿 불가능 이유
패턴:
not viable: 이 후보는 사용 불가requires X arguments, but Y were provided: 인자 개수 불일치'this' argument has type 'const T': const 불일치no known conversion from 'X' to 'Y': 타입 변환 불가
”note: because” 읽는 법
note: because 'std::unique_ptr<int>' does not satisfy 'is_copy_constructible'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
실제 원인 (이 부분만 읽으면 됨)
핵심: because 뒤의 내용이 진짜 원인입니다.
”required from” 읽는 법
note: in instantiation of function template specialization 'std::sort<...>' requested here
note: required from 'std::__sort<...>'
note: required from 'std::__introsort_loop<...>'
의미: 템플릿 인스턴스화 체인. 건너뛰어도 됩니다.
도구 활용
Compiler Explorer (godbolt.org)
1. godbolt.org 접속
2. 에러 코드 붙여넣기
3. 컴파일러 선택 (Clang 권장)
4. 에러 메시지 확인
장점:
- 여러 컴파일러로 동시 테스트
- Clang의 깔끔한 에러 메시지 확인
- 온라인에서 빠르게 테스트
Clang-Tidy
# 템플릿 관련 경고 활성화
clang-tidy main.cpp -- -std=c++17
# 체크 항목
# - bugprone-forwarding-reference-overload
# - readability-inconsistent-declaration-parameter-name
C++ Insights (cppinsights.io)
템플릿이 어떻게 인스턴스화되는지 확인
예제:
// 입력
template <typename T>
T add(T a, T b) { return a + b; }
auto x = add(1, 2);
// 출력 (인스턴스화된 코드)
int add<int>(int a, int b) { return a + b; }
int x = add<int>(1, 2);
정리
에러 메시지 읽는 3단계
- 첫 줄: 내 코드의 어느 줄에서 에러가 났는지
- “note: because”: 왜 실패했는지 (실제 원인)
- “candidate” 목록: 각 오버로드가 왜 안 맞는지
자주 나오는 에러 요약
| 에러 메시지 | 의미 | 해결법 |
|---|---|---|
no matching function | 오버로드를 찾지 못함 | 타입·인자 개수 확인 |
ambiguous call | 여러 오버로드가 동일하게 매칭 | 명시적 캐스팅 또는 오버로드 추가 |
no type named 'type' | 타입 트레이트 실패 | SFINAE 또는 Concepts 사용 |
static assertion failed | static_assert 실패 | 제약 조건 확인 |
cannot convert | 타입 변환 불가 | 올바른 타입 전달 |
incomplete type | 전방 선언만 있음 | 정의 포함 또는 포인터 사용 |
deduction failed | 템플릿 인자 추론 실패 | 명시적 타입 지정 |
invalid operands | 연산자 미정의 | operator 오버로드 |
핵심 규칙
- 첫 줄과 “because”만 읽으세요 (중간 100줄은 무시)
- Clang으로 에러를 확인하세요 (가장 읽기 쉬움)
- Concepts를 사용하세요 (C++20, 에러 메시지 10배 단축)
- 최소 재현 코드를 만드세요 (복잡한 코드에서 에러 원인 분리)
- candidate 목록을 확인하세요 (가장 가까운 오버로드 찾기)
구현 체크리스트
템플릿 에러를 빠르게 해결하기 위한 체크리스트:
- 에러 메시지의 첫 줄에서 내 코드의 위치를 확인했는가?
- “note: because” 부분에서 실제 원인을 찾았는가?
- candidate 목록에서 가장 가까운 오버로드를 확인했는가?
- 타입이 제약 조건을 만족하는지 확인했는가?
- Clang으로 에러 메시지를 다시 확인했는가?
- 최소 재현 코드로 문제를 단순화했는가?
- C++20 Concepts 사용을 고려했는가?
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++20 Concepts | 템플릿 에러 메시지를 읽기 쉽게 만드는 방법
- C++ 템플릿 기초 | 제네릭 프로그래밍 초보자 가이드
- C++ SFINAE | “Substitution Failure Is Not An Error” 가이드
- C++ 컴파일러 비교 | GCC vs Clang vs MSVC 에러 메시지 차이
자주 묻는 질문 (FAQ)
Q. 에러 메시지를 텍스트 파일로 저장하려면?
A. 컴파일러 출력을 리다이렉트하세요.
# GCC/Clang
g++ main.cpp 2> error.txt
# MSVC (Visual Studio)
# 출력 창에서 우클릭 → "모두 복사"
Q. 템플릿 에러를 컴파일 타임에 더 일찍 잡으려면?
A. static_assert와 Concepts를 함께 사용하세요.
template <typename T>
void process(T value) {
static_assert(std::is_integral_v<T>, "T must be integral");
// ...
}
Q. 외부 라이브러리의 템플릿 에러는 어떻게 읽나요?
A. 내 코드의 호출 지점을 먼저 찾으세요. “required from here”를 역순으로 따라가면 내 코드가 나옵니다.
/usr/include/boost/... ← 라이브러리 내부 (무시)
note: required from here
/usr/include/boost/... ← 라이브러리 내부 (무시)
note: required from here
main.cpp:42: ← 여기! 내 코드
고급 주제: 템플릿 메타프로그래밍 에러
SFINAE 에러 패턴
// SFINAE: Substitution Failure Is Not An Error
template <typename T>
auto getSize(const T& container) -> decltype(container.size()) {
return container.size();
}
template <typename T, size_t N>
size_t getSize(const T (&arr)[N]) {
return N;
}
// 사용
std::vector<int> vec;
getSize(vec); // ✅ 첫 번째 오버로드 선택
int arr[5];
getSize(arr); // ✅ 두 번째 오버로드 선택 (첫 번째는 SFINAE로 제외)
enable_if 에러
// ❌ 에러 코드
template <typename T,
typename = std::enable_if_t<std::is_integral_v<T>>>
void print(T value) {
std::cout << value << '\n';
}
print(3.14); // double은 integral이 아님
// error: no matching function for call to 'print'
// note: candidate template ignored: requirement 'is_integral_v<double>' was not satisfied
해결: Concepts로 대체 (C++20).
// ✅ C++20
template <std::integral T>
void print(T value) {
std::cout << value << '\n';
}
마치며
템플릿 에러 메시지는 처음에는 압도적이지만, 읽는 패턴을 익히면 5분 안에 원인을 찾을 수 있습니다.
핵심 전략:
- 첫 줄과 “because”만 읽기 (중간은 무시)
- Clang으로 에러 확인 (가장 읽기 쉬움)
- Concepts 사용 (C++20, 에러 메시지 10배 단축)
- 최소 재현 코드 작성 (복잡한 코드 단순화)
이 가이드를 북마크해 두고, 템플릿 에러가 나올 때마다 참고하세요. 에러 메시지를 읽는 능력은 C++ 숙련도를 크게 높여줍니다.
다음 단계: 템플릿 에러를 해결했다면, C++20 Concepts 완벽 가이드에서 더 안전한 템플릿 코드 작성법을 배워보세요.
관련 글
- C++ 시리즈 전체 보기
- C++ Adapter Pattern 완벽 가이드 | 인터페이스 변환과 호환성
- C++ ADL |
- C++ Aggregate Initialization |