문제

무엇 explicit 키워드 의미에서는 C++?

도움이 되었습니까?

해결책

컴파일러는 매개 변수를 함수로 해결하기 위해 하나의 암시 적 변환을 만들 수 있습니다. 이것이 의미하는 바는 컴파일러가 단일 매개 변수 매개 변수에 대한 올바른 유형을 얻으려면 한 유형에서 다른 유형으로 변환합니다.

다음은 암시 적 변환에 사용할 수있는 생성자가있는 예제 클래스입니다.

class Foo
{
public:
  // single parameter constructor, can be used as an implicit conversion
  Foo (int foo) : m_foo (foo) 
  {
  }

  int GetFoo () { return m_foo; }

private:
  int m_foo;
};

다음은 다음을 수행하는 간단한 기능입니다 Foo 물체:

void DoBar (Foo foo)
{
  int i = foo.GetFoo ();
}

그리고 여기 어디에 있습니다 DoBar 기능이 호출됩니다.

int main ()
{
  DoBar (42);
}

논쟁은 아닙니다 Foo 대상이지만 int. 그러나 생성자가 있습니다 Foo 그것은 필요합니다 int 따라서이 생성자를 사용하여 매개 변수를 올바른 유형으로 변환 할 수 있습니다.

컴파일러는 각 매개 변수에 대해이 작업을 한 번 수행 할 수 있습니다.

접두사 explicit 생성자에 대한 키워드는 컴파일러가 암시 적 변환에 해당 생성자를 사용하지 못하게합니다. 위의 클래스에 추가하면 함수 호출에서 컴파일러 오류가 발생합니다. DoBar (42). 이제 명시 적으로 전환을 요구해야합니다. DoBar (Foo (42))

이 작업을 원하는 이유는 버그를 숨길 수있는 우발적 인 구성을 피하기 때문입니다. 고안 예 :

  • 당신은 있습니다 MyString(int size) 주어진 크기의 문자열을 구성하는 생성자가있는 클래스. 당신은 기능이 있습니다 print(const MyString&), 그리고 당신은 전화합니다 print(3) (때를 실제로 전화하기위한 것입니다 print("3")). "3"을 인쇄 할 것으로 예상되지만 대신 빈 길이 3의 빈 문자열을 인쇄합니다.

다른 팁

수업이 있다고 가정 해 봅시다 String:

class String {
public:
    String(int n); // allocate n bytes to the String object
    String(const char *p); // initializes object with char *p
};

이제 시도하면 :

String mystring = 'x';

캐릭터 'x' 암시 적으로 변환됩니다 int 그리고 그 다음 String(int) 생성자가 호출됩니다. 그러나 이것은 사용자가 의도 한 것이 아닙니다. 따라서 그러한 조건을 방지하기 위해 생성자를 다음과 같이 정의해야합니다. explicit:

class String {
public:
    explicit String (int n); //allocate n bytes
    String(const char *p); // initialize sobject with string p
};

C ++에서, 하나의 필요한 파라미터 만있는 생성자는 암시 적 변환 함수로 간주됩니다. 매개 변수 유형을 클래스 유형으로 변환합니다. 이것이 좋은 것인지 아닌지는 생성자의 의미에 달려 있습니다.

예를 들어 생성자가있는 문자열 클래스가있는 경우 String(const char* s), 그것은 아마도 당신이 원하는 것일 것입니다. 당신은 통과 할 수 있습니다 const char* 기대하는 기능에 String, 컴파일러는 자동으로 임시를 구성합니다 String 당신을위한 대상.

반면에, 생성자의 버퍼 클래스가있는 경우 Buffer(int size) 버퍼의 크기를 바이트로 가져 가면 컴파일러가 조용히 회전하는 것을 원하지 않을 것입니다. ints로 Buffer에스. 이를 방지하기 위해 생성자를 explicit 예어:

class Buffer { explicit Buffer(int size); ... }

그런 식으로,

void useBuffer(Buffer& buf);
useBuffer(4);

컴파일 타임 오류가됩니다. 임시를 통과하려는 경우 Buffer 객체, 당신은 그것을 명시 적으로해야합니다.

useBuffer(Buffer(4));

요약하면, 단일 매개 변수 생성자가 매개 변수를 클래스의 객체로 변환하는 경우 아마도 사용하고 싶지 않을 것입니다. explicit 예어. 그러나 단순히 단일 매개 변수를 가져 오는 생성자가있는 경우 explicit 컴파일러가 예기치 않은 변환으로 놀라운 것을 방지합니다.

이 답변은 다른 답변에서 다루지 않기 때문에 명시적인 생성자가 있거나없는 객체 생성에 관한 것입니다.

명시적인 생성자가없는 다음 클래스를 고려하십시오.

class Foo
{
public:
    Foo(int x) : m_x(x)
    {
    }

private:
    int m_x;
};

클래스 foo의 객체는 두 가지 방법으로 만들 수 있습니다.

Foo bar1(10);

Foo bar2 = 20;

구현에 따라, 클래스 FOO를 인스턴스화하는 두 번째 방식은 혼란 스러울 수 있습니다. 접두사 explicit 생성자에 대한 키워드는 컴파일러 오류가 생성됩니다. Foo bar2 = 20;.

그것은이다 대개 단일 연계 제작자를 선언하는 모범 사례 explicit, 구현이 구체적으로 금지하지 않는 한.

또한 생성자가 있습니다

  • 모든 매개 변수에 대한 기본 인수, OR
  • 두 번째 매개 변수에 대한 기본 인수가 있습니다

둘 다 단일 연계 생성자로 사용할 수 있습니다. 그래서 당신은 이것들을 만들고 싶을 수도 있습니다 explicit.

고의적으로 예를 들어 ~ 아니다 단일 연계 제작자를 명시 적으로 만들고 싶다. 이것 대답). 그러한 경우, 객체를 만듭니다 add_x add30 = 30; 아마도 말이 될 것입니다.

여기 명백한 생성자에 대한 좋은 글입니다.

그만큼 explicit 키워드는 변환 생성자를 비 전환 생성자로 만듭니다. 결과적으로 코드는 오류가 적습니다.

키워드 explicit 동반

  • 첫 번째 (모든 유일한) 매개 변수를 유형 X로 암시 적으로 변환하는 데 사용할 수없는 클래스 X의 생성자

c ++ [class.conv.ctor

1) 함수-사양이없는 명시 적 명시 적으로 선언 된 생성자는 매개 변수 유형에서 클래스 유형으로 변환을 지정합니다. 이러한 생성자를 변환 생성자라고합니다.

2) 명시 적 생성자는 비외식 생성자와 같은 물체를 구성하지만 직접 이니셜 화 구문 (8.5) 또는 캐스트 (5.2.9, 5.4)가 명시 적으로 사용되는 경우에만 그렇게합니다. 기본 생성자는 명시 적 생성자 일 수 있습니다. 이러한 생성자는 기본 초기화 또는 부가 가치 화를 수행하는 데 사용됩니다 (8.5).

  • 또는 직접 초기화 및 명시 적 변환을 위해서만 고려되는 변환 함수.

c ++ [class.conv.fct

2) 변환 함수는 명시적일 수 있으며 (7.1.2),이 경우 직접 시작을위한 사용자 정의 변환 (8.5)으로만 간주됩니다. 그렇지 않으면 사용자 정의 변환은 할당 및 초기화에 사용되도록 제한되지 않습니다.

개요

명시 적 변환 기능 및 생성자는 명시 적 변환 (직접 초기화 또는 명시 적 캐스트 작업)에만 사용할 수 있으며, 비외식 생성자 및 변환 기능은 명시 적 변환뿐만 아니라 암시 적으로 사용될 수 있습니다.

/*
                                 explicit conversion          implicit conversion

 explicit constructor                    yes                          no

 constructor                             yes                          yes

 explicit conversion function            yes                          no

 conversion function                     yes                          yes

*/

구조 사용 예 X, Y, Z 그리고 기능 foo, bar, baz:

작은 구조와 기능 설정을 살펴보고 explicit 그리고 비explicit 전환.

struct Z { };

struct X { 
  explicit X(int a); // X can be constructed from int explicitly
  explicit operator Z (); // X can be converted to Z explicitly
};

struct Y{
  Y(int a); // int can be implicitly converted to Y
  operator Z (); // Y can be implicitly converted to Z
};

void foo(X x) { }
void bar(Y y) { }
void baz(Z z) { }

생성자에 관한 예 :

함수 인수 변환 :

foo(2);                     // error: no implicit conversion int to X possible
foo(X(2));                  // OK: direct initialization: explicit conversion
foo(static_cast<X>(2));     // OK: explicit conversion

bar(2);                     // OK: implicit conversion via Y(int) 
bar(Y(2));                  // OK: direct initialization
bar(static_cast<Y>(2));     // OK: explicit conversion

목표 초기화 :

X x2 = 2;                   // error: no implicit conversion int to X possible
X x3(2);                    // OK: direct initialization
X x4 = X(2);                // OK: direct initialization
X x5 = static_cast<X>(2);   // OK: explicit conversion 

Y y2 = 2;                   // OK: implicit conversion via Y(int)
Y y3(2);                    // OK: direct initialization
Y y4 = Y(2);                // OK: direct initialization
Y y5 = static_cast<Y>(2);   // OK: explicit conversion

전환 기능에 관한 예 :

X x1{ 0 };
Y y1{ 0 };

함수 인수 변환 :

baz(x1);                    // error: X not implicitly convertible to Z
baz(Z(x1));                 // OK: explicit initialization
baz(static_cast<Z>(x1));    // OK: explicit conversion

baz(y1);                    // OK: implicit conversion via Y::operator Z()
baz(Z(y1));                 // OK: direct initialization
baz(static_cast<Z>(y1));    // OK: explicit conversion

목표 초기화 :

Z z1 = x1;                  // error: X not implicitly convertible to Z
Z z2(x1);                   // OK: explicit initialization
Z z3 = Z(x1);               // OK: explicit initialization
Z z4 = static_cast<Z>(x1);  // OK: explicit conversion

Z z1 = y1;                  // OK: implicit conversion via Y::operator Z()
Z z2(y1);                   // OK: direct initialization
Z z3 = Z(y1);               // OK: direct initialization
Z z4 = static_cast<Z>(y1);  // OK: explicit conversion

왜 사용 explicit 변환 기능 또는 생성자?

전환 생성자 및 비외 전환 기능은 모호성을 도입 할 수 있습니다.

구조를 고려하십시오 V, 컨버터블 int, 구조 U 암시 적으로 구성 가능 V 그리고 함수 f 과부하 U 그리고 bool 각기.

struct V {
  operator bool() const { return true; }
};

struct U { U(V) { } };

void f(U) { }
void f(bool) {  }

전화 f 유형의 객체를 통과하면 모호합니다 V.

V x;
f(x);  // error: call of overloaded 'f(V&)' is ambiguous

컴파일러는 생성자를 사용하는 것을 알지 못합니다. U 또는 변환 기능을 변환 할 수 있습니다 V 전달하기위한 유형으로 반대합니다 f.

생성자 중 하나 인 경우 U 또는 변환 기능 V 할 것입니다 explicit, 비 침식 변환 만 고려되기 때문에 모호성이 없을 것입니다. 둘 다 명시 적으로 전화를 걸면 f 유형의 객체 사용 V 명시적인 변환 또는 캐스트 작업을 사용하여 수행해야합니다.

전환 생성자 및 비외 전환 기능은 예상치 못한 동작으로 이어질 수 있습니다.

일부 벡터 인쇄 기능을 고려하십시오.

void print_intvector(std::vector<int> const &v) { for (int x : v) std::cout << x << '\n'; }

벡터의 크기 구성자가 명시 적이 아닌 경우 다음과 같은 기능을 호출 할 수 있습니다.

print_intvector(3);

그러한 전화에서 무엇을 기대합니까? 하나의 줄이 포함되어 있습니다 3 또는 3 줄이 포함되어 있습니다 0? (두 번째는 일어나는 일입니다.)

클래스 인터페이스에서 명시적인 키워드를 사용하면 인터페이스 사용자가 원하는 변환에 대해 명시 적으로 시행됩니다.

Bjarne Stroustrup이 말한 것처럼 ( "C ++ 프로그래밍 언어", 4th ed., 35.2.1, pp. 1011) std::duration 평범한 숫자로 암시 적으로 구성 될 수 없습니다.

당신이 무슨 뜻인지 알면 그것에 대해 명시하십시오.

명시적인 변환 생성자 (C ++ 만 해당)

명시 적 기능 지정자는 원치 않는 암시 적 유형 변환을 제어합니다. 클래스 선언 내에서 생성자의 선언에만 사용할 수 있습니다. 예를 들어, 기본 생성자를 제외하고 다음 클래스의 생성자는 변환 생성자입니다.

class A
{
public:
    A();
    A(int);
    A(const char*, int = 0);
};

다음 선언은 합법적입니다.

A c = 1;
A d = "Venditti";

첫 번째 선언은 동일합니다 A c = A( 1 );.

클래스의 생성자를 AS로 선언하는 경우 explicit, 이전 선언은 불법입니다.

예를 들어, 수업을 다음과 같이 선언하는 경우

class A
{
public:
    explicit A();
    explicit A(int);
    explicit A(const char*, int = 0);
};

클래스 유형의 값과 일치하는 값 만 할당 할 수 있습니다.

예를 들어, 다음 진술은 합법적입니다.

  A a1;
  A a2 = A(1);
  A a3(1);
  A a4 = A("Venditti");
  A* p = new A(1);
  A a5 = (A)1;
  A a6 = static_cast<A>(1);

explicit-키워드를 사용할 수 있을 적용 생성자를 호출할 수 명시적으로.

class C{
public:
    explicit C(void) = default;
};

int main(void){
    C c();
    return 0;
}

explicit-키워드 앞에서 생성자 C(void) 컴파일러에만 명시적으로 호출이 생성자를 허용됩니다.

explicit-키워드를 사용할 수도 있습에서 사용자 정의 형식의 캐스팅 연산자

class C{
public:
    explicit inline operator bool(void) const{
        return true;
    }
};

int main(void){
    C c;
    bool b = static_cast<bool>(c);
    return 0;
}

여기서, explicit-키워드를 적용만 명시적으로 캐스팅하는 유효한 것,그래서 bool b = c; 것이 잘못된 이 경우입니다.이 같은 상황에서 explicit-키워드를 도울 수 있는 프로그래머를 피하는 암시적,의도하지 않은 캐스팅.이 사용되었습에서 표준화 C++11.

CPP 참조는 항상 도움이됩니다 !!! 명시 적 지정자에 대한 자세한 내용은 찾을 수 있습니다 여기. 봐야 할 수도 있습니다 암시 적 전환 그리고 사본 초기화 도.

한눈에

명시 적 지정자는 생성자 또는 변환 함수 (C ++ 11 이후)가 암시 적 변환 또는 사본 초기화를 허용하지 않음을 지정합니다.

다음과 같이 예 :

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) cout << "true" << endl; // 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 (b5) cout << "true" << endl; // 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
}

이것은 이미 논의되었습니다 (명백한 생성자는 무엇입니까?). 그러나 나는 여기에서 발견 된 자세한 설명이 부족하다고 말해야합니다.

게다가, 이미 언급 한 바와 같이 하나의 인수 생성자 (Arg2, Arg3, ...에 대한 기본값을 가진 것 포함)를 만드는 것은 항상 좋은 코딩 관행입니다. 항상 C ++와 마찬가지로 : 그렇지 않다면 - 당신은 당신이 원했을 것입니다 ...

수업의 또 다른 모범 사례는 실제로 구현 해야하는 경우 사본 구성 및 할당을 비공개로 만드는 것입니다 (일명 비활성화). 이렇게하면 C ++가 기본적으로 생성 할 메소드를 사용할 때 최종 포인터 사본을 피할 수 있습니다. 이를 수행하는 다른 방법은 Boost :: Copyable에서 파생됩니다.

생성자는 암시 적 변환을 추가합니다. 이 암시 적 변환을 억제하려면 매개 변수가있는 생성자를 명시 적으로 선언해야합니다.

C ++ 11에서는 이러한 키워드로 "Operator Type ()"을 지정할 수도 있습니다. http://en.cppreference.com/w/cpp/language/explicit 이러한 사양을 사용하면 명시 적 변환 및 객체의 직접 초기화 측면에서 연산자를 사용할 수 있습니다.

추신 : 사용자가 정의한 변환을 사용하면 (생성자 및 유형 변환 연산자를 통해) 사용 된 한 레벨의 암시 적 변환 만 허용됩니다. 그러나이 전환을 다른 언어 변환과 결합 할 수 있습니다.

  • UP 적분 순위 (char to int, float to double);
  • Standart 변환 (int to double);
  • 객체의 포인터를 기본 클래스 및 무효로 변환합니다*;
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top