explicit 지정자

explicit 지정자

생성자나 변환 함수(conversion function)(C++11 이후), 추론 가이드(deduction guide)(C++17 이후)를 explicit으로 만드는 지정자예요. explicit이 붙은 함수는 암시적 변환과 복사 초기화에 사용될 수 없어요. "이 타입으로 자동으로 바꿔주지 마"라고 말해주는 역할이에요.

출처: cppreference

본문

문법

explicit                    (1)
explicit ( expression )     (2)   (C++20 이후)
  • expressionbool 타입으로 문맥 변환된 상수 표현식(contextually converted constant expression).
  1. 생성자나 변환 함수(C++11 이후) 또는 추론 가이드(C++17 이후)가 explicit임을 지정해요. 즉 암시적 변환과 복사 초기화에 사용될 수 없어요.
  2. (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이라 직접 초기화(괄호 () 또는 중괄호 {})만 받아들인다는 거예요.

더 알아보기 (Learn more)