explicit 지정자
explicit 지정자
생성자나 변환 함수(conversion function)(C++11 이후), 추론 가이드(deduction guide)(C++17 이후)를 explicit으로 만드는 지정자예요. explicit이 붙은 함수는 암시적 변환과 복사 초기화에 사용될 수 없어요. "이 타입으로 자동으로 바꿔주지 마"라고 말해주는 역할이에요.
출처: cppreference
본문
문법
explicit (1)
explicit ( expression ) (2) (C++20 이후)
expression—bool타입으로 문맥 변환된 상수 표현식(contextually converted constant expression).
- 생성자나 변환 함수(C++11 이후) 또는 추론 가이드(C++17 이후)가 explicit임을 지정해요. 즉 암시적 변환과 복사 초기화에 사용될 수 없어요.
- (C++20 이후) explicit 지정자는 상수 표현식과 함께 쓸 수 있어요. 그 상수 표현식이
true로 평가될 때만 그 함수는 explicit이 돼요.
explicit 지정자는 그 클래스 정의 안에서 생성자나 변환 함수의 선언의 decl-specifier-seq 안에만 나타날 수 있어요 (C++11 이후).
Notes
단일 비-기본 매개변수(C++11 이전)를 가진 생성자가 함수 지정자 explicit 없이 선언되면, 그 생자를 **변환 생성자(converting constructor)**라고 불러요.
생성자(복사/이동 제외)와 사용자 정의 변환 함수 둘 다 함수 템플릿일 수 있으며, explicit의 의미는 변하지 않아요.
(C++20 이후) explicit 뒤에 오는 ( 토큰은 항상 explicit 지정자의 일부로 파싱돼요:
struct S
{
explicit (S)(const S&); // error in C++20, OK in C++17
explicit (operator int)(); // error in C++20, OK in C++17
};
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_conditional_explicit |
201806L |
(C++20) | 조건부 explicit |
키워드
explicit
예제
여기서 확인할 것: 같은 모양의 생성자/변환 함수에 대해, explicit이 있느냐 없느냐에 따라 어떤 초기화가 허용되는지 대비해 보는 코드예요.
struct A
{
A(int) {} // converting constructor
A(int, int) {} // converting constructor (C++11)
operator bool() const { return true; }
};
struct B
{
explicit B(int) {}
explicit B(int, int) {}
explicit operator bool() const { return true; }
};
int main()
{
A a1 = 1; // OK: copy-initialization selects A::A(int)
A a2(2); // OK: direct-initialization selects A::A(int)
A a3 {4, 5}; // OK: direct-list-initialization selects A::A(int, int)
A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
A a5 = (A)1; // OK: explicit cast performs static_cast
if (a1) { } // OK: A::operator bool()
bool na1 = a1; // OK: copy-initialization selects A::operator bool()
bool na2 = static_cast<bool>(a1); // OK: static_cast performs direct-initialization
// B b1 = 1; // error: copy-initialization does not consider B::B(int)
B b2(2); // OK: direct-initialization selects B::B(int)
B b3 {4, 5}; // OK: direct-list-initialization selects B::B(int, int)
// B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int, int)
B b5 = (B)1; // OK: explicit cast performs static_cast
if (b2) { } // OK: B::operator bool()
// bool nb1 = b2; // error: copy-initialization does not consider B::operator bool()
bool nb2 = static_cast<bool>(b2); // OK: static_cast performs direct-initialization
[](...){}(a4, a5, na1, na2, b5, nb2); // suppresses “unused variable” warnings
}
여기서 핵심은 A는 = 1 같은 복사 초기화도 받아들이지만, B는 explicit이라 직접 초기화(괄호 () 또는 중괄호 {})만 받아들인다는 거예요.