etc./StackOverFlow

명시적 키워드는 무엇을 의미합니까?

청렴결백한 만능 재주꾼 2021. 10. 9. 02:27
반응형

질문자 :Skizz


C++에서 explicit 키워드는 무엇을 의미합니까?



컴파일러는 매개변수를 함수로 해석하기 위해 하나의 암시적 변환을 수행할 수 있습니다. 이것이 의미하는 바는 컴파일러가 매개변수에 대한 올바른 유형을 얻기 위해 단일 매개변수 로 호출 가능한 생성자를 사용하여 한 유형에서 다른 유형으로 변환할 수 있다는 것입니다.

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

 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 입니다. 그러나 int 를 취하는 Foo 용 생성자가 있으므로 이 생성자를 사용하여 매개변수를 올바른 유형으로 변환할 수 있습니다.

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

explicit 키워드를 접두사로 지정하면 컴파일러가 암시적 변환에 해당 생성자를 사용하지 못하게 됩니다. DoBar (42) 함수 호출에서 컴파일러 오류가 생성됩니다. DoBar (Foo (42)) 하여 명시적으로 변환을 호출해야 합니다.

이렇게 하는 이유는 버그를 숨길 수 있는 우발적인 구성을 피하기 위함입니다.
고안된 예:

  • 주어진 크기의 문자열을 생성하는 생성자가 있는 MyString print(const MyString&) 함수(및 오버로드 print (char *string) )가 있고 print(3) 을 호출합니다( 실제로 print("3") 을 호출하려고 할 때). "3"을 인쇄할 것으로 예상하지만 대신 길이가 3인 빈 문자열을 인쇄합니다.

Community Wiki

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 };

Eddie

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

String(const char* s) 가 있는 문자열 클래스가 있는 경우 이것이 바로 원하는 것일 수 있습니다. String 기대하는 함수에 const char* 를 전달할 수 있으며 컴파일러는 자동으로 임시 String 객체를 생성합니다.

Buffer(int size) 생성자가 버퍼의 크기를 바이트 단위로 사용하는 버퍼 클래스가 있는 경우 intBuffer 로 바꾸는 것을 원하지 않을 것입니다. explicit 키워드를 사용하여 생성자를 선언합니다.

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

그런 식으로,

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

컴파일 타임 오류가 됩니다. Buffer 객체를 전달하려면 명시적으로 전달해야 합니다.

 useBuffer(Buffer(4));

요약하면 단일 매개변수 생성자가 매개변수를 클래스의 객체로 변환하는 경우 explicit 키워드를 사용하고 싶지 않을 것입니다. 그러나 단순히 단일 매개변수를 사용하는 생성자가 있는 경우 컴파일러가 예기치 않은 변환으로 사용자를 놀라게 하지 않도록 explicit


cjm

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 과부하 Ubool 각각있다.

 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 이면 비명시적 변환만 고려되므로 모호성이 없습니다. 둘 다 명시적이면 V 유형의 개체를 사용하여 f 대한 호출을 명시적 변환 또는 캐스트 작업을 사용하여 수행해야 합니다.

변환 생성자와 비명시적 변환 함수로 인해 예기치 않은 동작이 발생할 수 있습니다.

일부 벡터를 인쇄하는 함수를 고려하십시오.

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

벡터의 크기 생성자가 명시적이지 않은 경우 다음과 같이 함수를 호출할 수 있습니다.

 print_intvector(3);

그러한 부름에서 무엇을 기대하겠습니까? 0 3 또는 3개의 행을 포함하는 한 줄 ? (두 번째 것은 어디에서 무슨 일이 일어나는지입니다.)

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

std::duration 이 일반 숫자에서 암시적으로 생성될 수 없는 이유에 대해 ("The C++ Programming Language", 4th Ed., 35.2.1, pp. 1011) 다음과 같이 말했습니다.

무슨 말인지 안다면 분명히 말하십시오.


Pixelchemist

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

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

 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 선언하는 것이 일반적으로 좋은 방법입니다.

다음과 같은 생성자에 유의하십시오.

  • 모든 매개변수에 대한 기본 인수 또는
  • 두 번째 매개변수에 대한 기본 인수 이후

둘 다 단일 인수 생성자로 사용할 수 있습니다. explicit 만들고 싶을 수 있습니다.

단일 인수 생성자를 명시적으로 만들고 싶지 않은 경우의 예 는 펑터를 생성하는 경우입니다(이 답변에 선언된 'add_x' 구조체 참조). add_x add30 = 30; 과 같이 객체를 생성합니다. 아마 이해가 될 것입니다.

다음 은 명시적 생성자에 대한 좋은 글입니다.


Gautam

explicit 키워드는 변환 생성자를 비변환 생성자로 만듭니다. 결과적으로 코드는 오류가 덜 발생합니다.


SankararaoMajji

explicit -keyword는 생성자가 명시적 으로 호출되도록 강제하는 데 사용할 수 있습니다.

 class C{ public: explicit C(void) = default; }; int main(void){ C c(); return 0; }

C(void) 앞의 explicit -keyword는 이 생성자에 대한 명시적 호출만 허용됨을 컴파일러에 알립니다.

explicit -keyword는 사용자 정의 유형 캐스트 연산자에서도 사용할 수 있습니다.

 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 -keyword는 명시적 캐스트만 유효하도록 강제하므로 bool b = c; 이 경우 유효하지 않은 캐스트가 됩니다. explicit -keyword와 같은 상황에서 프로그래머는 의도하지 않은 암시적 캐스트를 피할 수 있습니다. 이 사용법은 C++11 에서 표준화되었습니다.


Helixirr

Cpp Reference는 항상 도움이 됩니다!!! 명시적 지정자에 대한 세부 정보는 여기 에서 찾을 수 있습니다. 암시적 변환복사 초기화 도 살펴봐야 할 수 있습니다.

한눈에

명시적 지정자는 생성자 또는 변환 함수(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 }

selfboot

이미 언급한 대로 arg2 , arg3 ,...에 대한 기본값이 있는 생성자 포함)를 만드는 것은 항상 좋은 코딩 방법입니다. C++에서 항상 그렇듯이: 그렇게 하지 않으면 - 그렇게 했으면 하는 바램이 있을 것입니다...

클래스에 대한 또 다른 좋은 방법은 실제로 구현해야 하는 경우가 아니면 복사 생성 및 할당을 비공개(일명 비활성화)로 만드는 것입니다. 이렇게 하면 C++에서 기본적으로 생성하는 메서드를 사용할 때 포인터의 최종 복사본이 생기는 것을 방지할 수 있습니다. 이를 수행하는 다른 방법은 boost::noncopyable 에서 파생됩니다.


fmuecke

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

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

PS USER에 의해 정의된 변환을 사용할 때(생성자와 유형 변환 연산자를 통해) 암시적 변환의 한 수준만 사용할 수 있습니다. 그러나 이 변환을 다른 언어 변환과 결합할 수 있습니다.

  • 최대 적분 순위(char에서 int로, float에서 double로);
  • 표준 변환(int에서 double로);
  • 객체의 포인터를 기본 클래스 및 void*로 변환합니다.

bruziuz

출처 : http:www.stackoverflow.com/questions/121162/what-does-the-explicit-keyword-mean

반응형