핵심 요약
C++ explicit은 생성자, 변환 함수(C++11~), 추론 가이드(C++17~)가 암시적 변환과 복사 초기화에 사용되지 못하도록 막는 지정자입니다. explicit이 없는 생성자는 변환 생성자로 동작하므로 f(5)처럼 원시 값이 의도치 않게 객체로 변환되어 전달될 수 있습니다. 따라서 단일 인자 생성자는 explicit을 먼저 고려하고, std::string s = "abc";처럼 변환이 자연스러운 경우에만 생략하는 것이 기본 원칙입니다. 조건문에서만 검사를 허용하는 explicit operator bool()(C++11)과 조건부 explicit(bool)(C++20)도 같은 원리의 확장입니다.
이 글에서는 g++ 14.2와 clang 19.1.7로 직접 컴파일한 결과를 바탕으로 explicit이 무엇을 막고 무엇을 허용하는지 정리해 드립니다. 암시적 변환 규칙 자체는 C++ 정수 승격 규칙 글을 참고해 주시기 바랍니다.
C++ explicit이란? 변환 생성자는 무엇인가?
면접형 한 줄 답: explicit은 생성자나 변환 함수가 암시적 변환과 복사 초기화에 쓰이지 못하게 막는 지정자이며, explicit 없이 선언된 생성자가 변환 생성자입니다.
- 공식 정의: cppreference에서는
explicit을 "Specifies that a constructor or conversion function (since C++11) or deduction guide (since C++17) is explicit, that is, it cannot be used for implicit conversions and copy-initialization."라고 설명합니다. - 변환 생성자: cppreference의 변환 생성자 문서에 따르면
explicit없이 선언된 생성자가 변환 생성자입니다. C++11 이전에는 기본값이 없는 단일 매개변수를 갖는 생성자를 가리켰으나, C++11부터는 매개변수가 여러 개인 non-explicit 생성자도 복사 리스트 초기화에 참여합니다. 예를 들어A a4 = {4, 5};는 허용되지만, 생성자가explicit으로 선언된 경우B b4 = {4, 5};는 컴파일 에러가 발생합니다. - explicit 생성자일 때의 동작:
B b1 = 1;과 같은 암시적 변환 및 복사 초기화는 컴파일 에러가 발생합니다. 반면B b2(2);,B b3{4, 5};와 같은 직접 초기화나 명시적 캐스트(B)1은 문제없이 컴파일됩니다. 또한explicit operator bool이 정의되어 있다면if (b2)처럼 조건식 맥락에서는 유효하지만,bool nb1 = b2;처럼 암시적으로 변환할 때는 에러가 발생하며,static_cast<bool>(b2)처럼 명시적으로 캐스팅해야 정상 동작합니다.

explicit을 붙이지 않으면 어떤 버그가 생기나?
면접형 한 줄 답: 객체를 직접 생성하지 않았는데도 int 같은 원시 값이 임시 객체로 조용히 변환되어 함수에 전달되며, 컴파일 경고조차 발생하지 않을 수 있습니다.
- 실제 버그 재현 (
b.cpp): 아래는 non-explicit 생성자를 사용한 예제입니다.g++ 14.2 -std=c++20 -Wall -Wextra환경에서 경고 없이 컴파일되고 정상 실행되었습니다.
실행 결과는 다음과 같습니다.#include <cstdio> struct Buffer { Buffer(int size) : size(size) {} int size; }; void resize(Buffer b) { std::printf("Buffer size = %d\n", b.size); } int main() { int count = 3; resize(count); }
코드 어디에서도Buffer size = 3Buffer객체를 명시적으로 생성하지 않았지만,int타입 변수인count가Buffer로 조용히 변환되어 전달되었습니다. - 표준 라이브러리의 설계:
std::vector<int> v = 10;이 컴파일되지 않는 이유도 이와 같습니다. 크기를 인자로 받는vector생성자가explicit으로 선언되어 있어 정수 하나로 원소 10개짜리 컨테이너가 의도치 않게 암시적으로 만들어지는 것을 방지합니다. - 실제 컴파일 결과:
g++ (Debian 14.2.0-19) 14.2.0과Debian clang 19.1.7에서-std=c++20 -fsyntax-only옵션으로 컴파일한a.cpp입니다.
g++ 14.2의 에러 출력입니다.#include <memory> #include <vector> struct Meters { explicit Meters(int v) : value(v) {} int value; }; void f(Meters m) {} int main() { Meters a{5}; // OK: direct-initialization f(Meters{5}); // OK Meters b = 5; // error f(5); // error std::vector<int> v = 10; // error std::unique_ptr<int> p = std::make_unique<int>(1); if (p) {} // OK: explicit operator bool in a condition bool ok = p; // error }
clang 19.1.7의 출력(첫 에러와 관련 노트 포함)입니다.a.cpp:8:16: error: conversion from ‘int’ to non-scalar type ‘Meters’ requested a.cpp:9:7: error: could not convert ‘5’ from ‘int’ to ‘Meters’ a.cpp:10:26: error: conversion from ‘int’ to non-scalar type ‘std::vector<int>’ requested a.cpp:13:15: error: cannot convert ‘std::unique_ptr<int>’ to ‘bool’ in initializationa.cpp:8:12: error: no viable conversion from 'int' to 'Meters' a.cpp:3:26: note: explicit constructor is not a candidate a.cpp:9:5: error: no matching function for call to 'f' a.cpp:10:22: error: no viable conversion from 'int' to 'std::vector<int>' - 컴파일러가 실제로 어떻게 변환하는지 확인하려면: C++ Insights 활용법 글에서 컴파일러가 임시 객체를 생성하는 과정을 직접 확인하실 수 있습니다.
explicit은 언제 붙이고 언제 빼야 하나?
면접형 한 줄 답: 단일 인자 생성자는 기본적으로 explicit을 먼저 고려하고, std::string s = "abc";처럼 변환이 개념적으로 자연스러울 때만 생략합니다.
- 기본 원칙: 단일 인자 생성자는 원칙적으로
explicit지정을 우선적으로 고려합니다. - 생략해도 되는 경우:
std::string의const char*생성자는 non-explicit으로 선언되어 있어std::string s = "abc";와 같은 자연스러운 변환이 가능합니다. explicit operator bool():if (p),while (p)와 같은 조건문 맥락에서의 평가만 허용하고,bool ok = p;나int x = p;와 같은 의도치 않은 암시적 변환은 방지합니다. 명시적 변환이 필요할 때는static_cast<bool>(p)를 사용하실 수 있습니다. 위a.cpp의std::unique_ptr가 대표적인 예입니다.- C++20 조건부
explicit(bool):explicit(expression)은 컴파일 타임 상수 bool 표현식이 true일 때만explicit으로 동작하며, 기능 테스트 매크로는__cpp_conditional_explicit(201806L)입니다. 아래는 g++ 14.2-std=c++20으로 컴파일한c.cpp입니다.
실제 컴파일 에러는 다음 한 줄뿐이었습니다.template <class T> struct Wrap { template <class U> explicit(!std::is_convertible_v<U, T>) Wrap(U u) : t(u) {} T t; }; struct S { explicit S(int) {} }; int main() { Wrap<long> a = 1; Wrap<S> b = 1; }c.cpp:7:44: error: conversion from ‘int’ to non-scalar type ‘Wrap<S>’ requestedint는long으로 암시적 변환이 가능하므로 조건식이 false가 되어explicit(false)로 평가되며, 그 결과Wrap<long> a = 1;은 문제없이 컴파일됩니다. 반면S의 생성자는explicit이어서int에서S로의 암시적 변환이 불가능하므로 조건식이 true가 되어explicit(true)로 동작하며, 오직Wrap<S> b = 1;만 컴파일 에러로 차단됩니다.

생성자 자체의 기본 개념은 C++ 복사 생성자와 이동 생성자 기본 글에 자세히 정리되어 있습니다.
면접에서 explicit은 어떻게 답하나?
면접형 한 줄 답: 아래 네 가지 핵심 질문을 정리해 두시면 explicit 관련 기술 면접 질문에 충분히 대비하실 수 있습니다.
Q1. C++에서 explicit 키워드의 역할과 사용하는 주된 이유는 무엇인가요?
explicit은 생성자, 변환 함수(C++11~), 추론 가이드(C++17~)가 암시적 변환과 복사 초기화에 사용되지 못하도록 막는 지정자입니다. 원시 값이나 다른 타입이 의도치 않게 객체로 변환되어 함수 호출이나 대입 연산에서 버그를 유발하는 것을 방지하고, 직접 초기화(Meters m{5};)나 명시적 캐스트 사용을 유도하기 위해 사용합니다.
Q2. explicit 생성자를 가진 클래스에서 허용되는 초기화와 차단되는 초기화의 차이는 무엇인가요?
B b = 1;, B b = {4, 5};, f(5)와 같은 복사 초기화 및 암시적 인자 전달은 컴파일 에러가 발생합니다. 반면 B b(2);, B b{4, 5};와 같은 직접 초기화와 (B)1, static_cast<B>(1)과 같은 명시적 캐스트는 정상적으로 허용됩니다.
Q3. 스마트 포인터나 래퍼 클래스에서 explicit operator bool()을 선언하는 이유는 무엇인가요?
if (ptr), while (ptr)처럼 조건문 맥락에서의 유효성 검사는 허용하면서도, bool ok = ptr;나 int x = ptr;처럼 의도치 않은 암시적 타입 변환이 일어나는 것을 방지하기 위해서입니다.
Q4. 단일 인자 생성자에서 explicit을 생략해도 되는 경우는 언제인가요?
std::string s = "abc";처럼 타입 변환이 개념적으로 자연스럽고 안전할 때입니다. 실제로 std::string의 const char* 생성자는 non-explicit으로 선언되어 있습니다. 다만 일반적인 개발 원칙으로는 단일 인자 생성자에 explicit 지정을 우선적으로 고려해야 합니다.
Summary
C++ explicit is a specifier that prevents constructors, conversion functions (since C++11), and deduction guides (since C++17) from participating in implicit conversions and copy-initialization. A constructor declared without explicit acts as a converting constructor, meaning a call like f(5) can silently convert a raw value into an object. The baseline guideline is to consider explicit first for single-argument constructors and omit it only when the conversion is conceptually natural, as in std::string s = "abc";. Contextual boolean conversions with explicit operator bool() (C++11) and conditional explicit(bool) (C++20) extend this exact principle.
This post examines what explicit blocks and what it permits, based on real compiler output from g++ 14.2 and clang 19.1.7. For the general rules governing implicit conversions, refer to the C++ integer promotion post (Korean).
What is C++ explicit? What is a converting constructor?
Interview one-liner: explicit prevents a constructor or conversion function from being used in implicit conversions and copy-initialization, and any constructor declared without it is a converting constructor.
- Official definition: cppreference specifies that
explicit"Specifies that a constructor or conversion function (since C++11) or deduction guide (since C++17) is explicit, that is, it cannot be used for implicit conversions and copy-initialization." - Converting constructor: According to the cppreference page on converting constructors, a constructor declared without
explicitis a converting constructor. Prior to C++11, this referred to a constructor with a single non-default parameter. Since C++11, multi-argument non-explicit constructors also participate in copy-list-initialization. For example,A a4 = {4, 5};compiles, but if the constructor is markedexplicit,B b4 = {4, 5};produces a compile error. - Behavior with an explicit constructor: Copy-initialization such as
B b1 = 1;triggers a compile error. Direct-initialization such asB b2(2);andB b3{4, 5};, as well as explicit casts like(B)1, are valid. Similarly, with anexplicit operator bool, evaluating the object in a condition likeif (b2)works, whereas an implicit conversion likebool nb1 = b2;fails with a compile error; explicit casting withstatic_cast<bool>(b2)is required.

What bugs can happen without explicit?
Interview one-liner: Without explicit, values such as an int can silently convert into a temporary object and get passed to a function, often without producing any compiler warning.
- A real bug demo (
b.cpp): The following example uses a non-explicit constructor. Underg++ 14.2 -std=c++20 -Wall -Wextra, it compiled cleanly without warnings and executed:
The output is:#include <cstdio> struct Buffer { Buffer(int size) : size(size) {} int size; }; void resize(Buffer b) { std::printf("Buffer size = %d\n", b.size); } int main() { int count = 3; resize(count); }
No statement explicitly created aBuffer size = 3Buffer, yet theintvariablecountwas silently converted into one. - Standard library design: This is precisely why
std::vector<int> v = 10;does not compile. The size-taking constructor ofstd::vectoris declaredexplicit, preventing a single integer from unintentionally initializing a 10-element container. - Actual compile output: Here is
a.cpp, compiled usingg++ (Debian 14.2.0-19) 14.2.0andDebian clang 19.1.7with-std=c++20 -fsyntax-only:
g++ 14.2 error output:#include <memory> #include <vector> struct Meters { explicit Meters(int v) : value(v) {} int value; }; void f(Meters m) {} int main() { Meters a{5}; // OK: direct-initialization f(Meters{5}); // OK Meters b = 5; // error f(5); // error std::vector<int> v = 10; // error std::unique_ptr<int> p = std::make_unique<int>(1); if (p) {} // OK: explicit operator bool in a condition bool ok = p; // error }
clang 19.1.7 output (first error and notes):a.cpp:8:16: error: conversion from ‘int’ to non-scalar type ‘Meters’ requested a.cpp:9:7: error: could not convert ‘5’ from ‘int’ to ‘Meters’ a.cpp:10:26: error: conversion from ‘int’ to non-scalar type ‘std::vector<int>’ requested a.cpp:13:15: error: cannot convert ‘std::unique_ptr<int>’ to ‘bool’ in initializationa.cpp:8:12: error: no viable conversion from 'int' to 'Meters' a.cpp:3:26: note: explicit constructor is not a candidate a.cpp:9:5: error: no matching function for call to 'f' a.cpp:10:22: error: no viable conversion from 'int' to 'std::vector<int>' - Inspecting compiler-generated conversions: To see how the compiler instantiates temporary objects under the hood, visit the C++ Insights post (Korean).
When should you use or omit explicit?
Interview one-liner: Consider explicit first for single-argument constructors, and omit it only when the conversion is conceptually natural, such as std::string s = "abc";.
- Default rule: For single-argument constructors, default to marking them
explicit. - When it is fine to omit: The
const char*constructor ofstd::stringis non-explicit, which allowsstd::string s = "abc";to work naturally. explicit operator bool(): It enables boolean checks in conditional contexts such asif (p)andwhile (p)while preventing unintended implicit conversions likebool ok = p;andint x = p;. Explicit conversions usingstatic_cast<bool>(p)remain available. Thestd::unique_ptrina.cppabove is an example.- C++20 conditional
explicit(bool):explicit(expression)makes the constructor explicit only when the constant bool expression evaluates to true; the feature-test macro is__cpp_conditional_explicit(201806L). Below isc.cpp, compiled with g++ 14.2-std=c++20:
The only compile error was this single line:template <class T> struct Wrap { template <class U> explicit(!std::is_convertible_v<U, T>) Wrap(U u) : t(u) {} T t; }; struct S { explicit S(int) {} }; int main() { Wrap<long> a = 1; Wrap<S> b = 1; }
Becausec.cpp:7:44: error: conversion from ‘int’ to non-scalar type ‘Wrap<S>’ requestedintconverts implicitly tolong, the condition evaluates toexplicit(false), andWrap<long> a = 1;compiles without issue. In contrast,Shas anexplicitconstructor, sointcannot implicitly convert toS; the condition evaluates toexplicit(true), blocking onlyWrap<S> b = 1;.

For the fundamentals of constructors themselves, see the copy and move constructor basics post (Korean).
How do you answer explicit questions in an interview?
Interview one-liner: Preparing the four questions below covers the vast majority of interview questions regarding explicit.
Q1. What is the role of the explicit keyword in C++, and why is it mainly used?
explicit stops a constructor, conversion function (since C++11), or deduction guide (since C++17) from participating in implicit conversions and copy-initialization. It prevents bugs from unintended conversions of raw values or other types into objects during function calls or assignments, enforcing direct-initialization (Meters m{5};) or explicit casts.
Q2. What is the difference between allowed and blocked initialization for a class with an explicit constructor?
Copy-initialization and implicit argument passing such as B b = 1;, B b = {4, 5};, and f(5) fail with compile errors. In contrast, direct-initialization such as B b(2); and B b{4, 5};, as well as explicit casts such as (B)1 and static_cast<B>(1), are permitted.
Q3. Why do smart pointers or wrapper classes declare explicit operator bool()?
It allows objects to be tested in conditional contexts like if (ptr) and while (ptr), while strictly blocking unsafe implicit conversions such as bool ok = ptr; or int x = ptr;.
Q4. When is it acceptable to omit explicit on a single-argument constructor?
When the conversion is conceptually natural and safe, such as std::string s = "abc";. The const char* constructor of std::string is declared non-explicit. The baseline rule still applies: always consider explicit first for single-argument constructors.
