[c++] 다른 컴파일러에 의해 호출되는 다른 캐스트 연산자

다음의 짧은 C ++ 프로그램을 고려하십시오.

#include <iostream>

class B {
public:
    operator bool() const {
        return false;
    }
};

class B2 : public B {
public:
    operator int() {
        return 5;
    }
};

int main() {
    B2 b;
    std::cout << std::boolalpha << (bool)b << std::endl;
}

다른 컴파일러에서 컴파일하면 다양한 결과를 얻습니다. Clang 3.4 및 GCC 4.4.7에서는 인쇄 true하는 반면 Visual Studio 2013에서는 인쇄합니다 false. 즉, (bool)b. 표준에 따른 올바른 행동은 무엇입니까?

내 이해에서 operator bool()더 변환을 필요로하지 않는다, 동안은 operator int()필요 intbool컴파일러는 첫 번째를 선택해야하므로, 변환. 않는 const그와 무언가를, 컴파일러에 의해 더 “비용”으로 간주 const를 변환 무엇입니까?

을 제거하면 const모든 컴파일러가 똑같이 false출력으로 생성 됩니다. 반면에 두 클래스를 함께 결합하면 (두 연산자가 동일한 클래스에 있음) 세 컴파일러 모두 true출력 을 생성 합니다.



답변

표준 상태 :

파생 클래스의 변환 함수는 두 함수가 동일한 형식으로 변환되지 않는 한 기본 클래스의 변환 함수를 숨기지 않습니다.

§12.3 [class.conv]

어떤 수단 operator bool에 의해 숨겨지지 않습니다 operator int.

표준 상태 :

오버로드 확인 중에 암시 된 개체 인수는 다른 인수와 구별 할 수 없습니다.

§13.3.3.1 [over.match.funcs]

이 경우 “묵시적 객체 인수” b는이며 유형은입니다 B2 &. operator bool필요 const B2 &하므로 컴파일러는에 const를 추가 b하여 호출해야합니다 operator bool. 이것은-다른 모든 것이 동등하다는- operator int더 나은 일치를 만듭니다 .

표준은 a static_cast(이 경우 C 스타일 캐스트가 수행하는 )가 다음 T과 같은 경우 유형 (이 경우 int)으로 변환 할 수 있다고 명시합니다.

선언 T t(e);은 일부 발명 된 임시 변수에 대해 잘 구성되어 t있습니다.

§5.2.9 [expr.static.cast]

따라서,이 int(A)에 전환 될 수있다 bool, A는 bool똑같이 전환 될 수있다 bool.

표준 상태 :

의 변환 함수 S및 기본 클래스가 고려됩니다. 내부에 숨겨지지 않은 비명 시적 변환 함수 S와 yield type T 또는 T표준 변환 시퀀스를 통해 유형으로 변환 할 수있는 유형 은 후보 함수입니다.

§13.3.1.5 [over.match.conv]

따라서 과부하 세트는 operator int및 로 구성됩니다 operator bool. 다른 모든 것이 동일 operator int하면 더 나은 일치입니다 (불변성을 추가 할 필요가 없기 때문에). 따라서 operator int선택해야합니다.

(아마 직관에 반하는) 표준은 다음 중 하나의 인수에 대한 변환 시퀀스를 제공하는 경우 (위에 설정된대로) 오버로드 세트에 추가 된 후 반환 유형 (즉, 이러한 연산자가 변환되는 유형)을 고려하지 않습니다. 그것들은 다른 인자에 대한 변환 시퀀스보다 우월합니다 (이 경우에는 불변성 때문에).

표준 상태 :

이러한 정의가 주어지면 실행 가능한 함수 F1은 모든 인수 i에 대해 ICSi (F1)가 ICSi (F2)보다 나쁜 변환 시퀀스가 ​​아니라면 다른 실행 가능한 함수 F2보다 더 나은 함수로 정의됩니다.

  • 일부 인수 j의 경우 ICSj (F1)이 ICSj (F2)보다 더 나은 변환 시퀀스이거나 그렇지 않은 경우
  • 컨텍스트는 사용자 정의 변환에 의한 초기화이며 반환 유형 F1에서 대상 유형 (즉, 초기화되는 엔티티 유형)으로의 표준 변환 시퀀스가 ​​반환 유형의 표준 변환 시퀀스보다 더 나은 변환 시퀀스입니다. 대상 유형에 대한 F2.

§13.3.3 [over.match.best]

이 경우 하나의 인수 (암시 적 this매개 변수) 만 있습니다. 변환 시퀀스에 대해 B2 &=> B2 &(호출 operator int)에 우수 B2 &=> const B2 &(호출 operator bool), 따라서 operator int실제로 직접 변환하지 않는다는 사실에 관계없이 과부하 세트로부터 선택된다 bool.


답변

짧은

변환 함수는 operator int()위에 연타 의해 선택된 operator bool() const때문에b const 정규화되지 않았기 반면 bool의 변환 연산자는 그렇습니다.

짧은 이유는 다음과 같이 변환 b할 때 과부하 해결을위한 후보 함수 (암시 적 객체 매개 변수 사용) bool

operator bool (B2 const &);
operator int (B2 &);

두 번째 것이 더 나은 경기입니다. b 는 const 규정이 아니기 입니다.

두 기능이 동일한 자격을 공유하는 경우 (둘 다 const 또는 아님)을operator bool 직접 변환을 제공하므로이 선택됩니다.

캐스트 표기법을 통한 변환, 단계별 분석

부울 ostream 삽입 기 (std :: basic_ostream :: operator << (bool val) as per [ostream.inserters.arithmetic])가로 변환 된 값으로 호출된다는 데 동의 b하면bool 우리가 전환 가능 파고 .

1. 캐스트 표현

b를 bool로 캐스트

(bool)b

평가하다

static_cast<bool>(b)

C ++ 11 / 4 5.4 [expr.cast] 보낸const_cast 적용 할 수 없다 (추가 또는 여기 CONST 제거되지 않음).

이 정적 변환은 당 허용되는 , C ++ 4분의 11 5.2.9 [expr.static.cast] , 경우에 bool t(b);발명 된 변수 t가 잘 형성되어 대한. 이러한 문을 C ++ 11, 8.5 / 15 [dcl.init]에 따라 직접 초기화라고 합니다.

2. 직접 초기화 bool t(b);

가장 적게 언급 된 표준 단락의 16 절 은 다음과 같이 말합니다 (강조 표시).

이니셜 라이저의 의미는 다음과 같습니다. 대상 유형은 초기화되는 개체 또는 참조의 유형이고 소스 유형은 이니셜 라이저 표현식의 유형입니다.

[…]

[…] 소스 유형 이 (가능하면 cv-qualified) 클래스 유형 인 경우 변환 함수 가 고려됩니다.

적용 가능한 변환 기능이 열거되고 과부하 해결을 통해 최상의 기능이 선택됩니다.

2.1 어떤 변환 기능을 사용할 수 있습니까?

사용 가능한 변환 함수는 다음 operator int ()operator bool() const같습니다. C ++ 11부터 12.3 / 5 [class.conv] 는 다음을 알려줍니다.

파생 클래스의 변환 함수는 두 함수가 동일한 형식으로 변환되지 않는 한 기본 클래스의 변환 함수를 숨기지 않습니다.

반면 C ++ 11 13.3.1.5/1 [over.match.conv] 주 :

S 및 기본 클래스의 변환 함수가 고려됩니다.

여기서 S는 변환 될 클래스입니다.

2.2 적용 가능한 변환 기능은 무엇입니까?

C ++ 11, 13.3.1.5/1 [over.match.conv] (강조 내) :

1 […] “cv1 T”가 초기화되는 객체의 유형이고 “cv S”가 초기화 표현식의 유형이고 S가 클래스 유형이라고 가정하면 후보 함수는 다음과 같이 선택됩니다. S 및 기본 클래스의 기능이 고려됩니다. S 및 yield type T 내에 숨겨지지 않은 비명 시적 변환 함수 또는 표준 변환 시퀀스를 통해 유형 T로 변환 할 수있는 유형 은 후보 함수입니다.

따라서 operator bool () const이 숨겨져되지 않기 때문에 적용 B2과를 산출한다 bool.

마지막 표준 견적에서 강조된 부분 은 표준 변환 시퀀스를 통해 bool로 변환 할 수있는 유형 operator int ()이기 때문에 사용하는 변환과 관련 int이 있습니다. 행 변환 int으로는 bool심지어 시퀀스 그러나 당 허용되는 일반 직접 변환 인 C ++ 11 4.12 / 1 conv.bool]

산술, 범위가 지정되지 않은 열거, 포인터 또는 멤버 유형에 대한 포인터의 prvalue는 bool 유형의 prvalue로 변환 될 수 있습니다. 0 값, 널 포인터 값 또는 널 멤버 포인터 값은 false로 변환됩니다. 다른 값은 true로 변환됩니다.

이것은 operator int ()또한 적용 가능 하다는 것을 의미합니다 .

2.3 어떤 변환 기능이 선택됩니까?

적절한 변환 함수의 선택은 과부하 해결 ( C ++ 11, 13.3.1.5/1 [over.match.conv] )을 통해 수행됩니다 .

과부하 해결은 호출 할 변환 기능을 선택하는 데 사용됩니다.

클래스 멤버 함수의 오버로드 해결과 관련하여 특별한 “특징”이 하나 있습니다. 암시 적 개체 매개 변수입니다.

C ++ 11 13.3.1 [over.match.funcs] ,

[…] 정적 및 비 정적 멤버 함수에는 모두 암시 적 개체 매개 변수가 있습니다 […]

여기서 4- 절에 따른 비 정적 멤버 함수에 대한이 매개 변수의 유형은 다음과 같습니다.

  • ref 한정자없이 또는 & ref 한정자를 사용하여 선언 된 함수에 대한 “cv X에 대한 lvalue 참조”

  • && ref 한정자로 선언 된 함수에 대한 “cv X에 대한 rvalue 참조”

여기서 X는 함수가 멤버 인 클래스이고 cv는 멤버 함수 선언에 대한 cv 자격입니다.

, 변환 함수에 의한 초기화에서 ( C ++ 11, 13.3.1.5/2 [over.match.conv]에 따라 ),

인수 목록에는 이니셜 라이저 표현식 인 하나의 인수가 있습니다. [참고 :이 인수는 변환 함수의 암시 적 개체 매개 변수와 비교됩니다. —end note]

과부하 해결을위한 후보 함수는 다음과 같습니다.

operator bool (B2 const &);
operator int (B2 &);

자격 변환이 필요하기 때문에 operator int ()유형의 상수가 아닌 개체를 사용하여 변환이 요청되는 경우 분명히 더 나은 일치 입니다.B2operator bool ()

두 변환 함수가 동일한 const 자격을 공유하면 해당 함수의 오버로드 해결이 더 이상 트릭을 수행하지 않습니다. 이 경우 전환 (시퀀스) 순위가 적용됩니다.

3. operator bool ()두 변환 함수가 동일한 const 자격을 공유 할 때 왜 선택됩니까?

행 변환 B2하려면 bool사용자 정의 변환 시퀀스 (인 C ++ 11 13.3.3.1.2 / 1 over.ics.user] )

사용자 정의 변환 시퀀스는 초기 표준 변환 시퀀스, 사용자 정의 변환, 두 번째 표준 변환 시퀀스로 구성됩니다.

[…] 사용자 정의 변환이 변환 함수에 의해 지정된 경우 초기 표준 변환 시퀀스는 소스 유형을 변환 함수의 암시 적 개체 매개 변수로 변환합니다.

C ++ 11, 13.3.3.2/3 [over.ics.rank]

[…]은 더 나은 변환 시퀀스와 더 나은 변환 관계를 기반으로 암시 적 변환 시퀀스의 부분 순서를 정의합니다.

[…] 사용자 정의 변환 시퀀스 U1은 동일한 사용자 정의 변환 함수 또는 생성자 또는 집계 초기화를 포함하고 U1의 두 번째 표준 변환 시퀀스가 ​​다음보다 나은 경우 다른 사용자 정의 변환 시퀀스 U2보다 나은 변환 시퀀스입니다. U2의 두 번째 표준 변환 시퀀스입니다.

두 번째 기준 변환 경우가 operator bool()있다 boolbool있는 경우는 제 표준 변환 반면 (신원 변환) operator int ()intbool부울 변환된다.

따라서 operator bool ()두 변환 함수가 동일한 const 자격을 공유하는 경우를 사용하는 변환 시퀀스 가 더 좋습니다.


답변

C ++ bool 유형에는 해당 값 1과 0과 함께 true 및 false의 두 가지 값이 있습니다. B2 클래스에 기본 클래스 (B)의 bool 연산자를 명시 적으로 호출하는 bool 연산자를 추가하면 본질적인 혼동을 피할 수 있습니다. 거짓으로. 여기 수정 된 프로그램이 있습니다. 그러면 연산자 bool은 연산자 int가 아니라 연산자 bool을 의미합니다.

#include <iostream>

class B {
public:
    operator bool() const {
        return false;
    }
};

class B2 : public B {
public:
    operator int() {
        return 5;
    }
    operator bool() {
        return B::operator bool();
    }
};

int main() {
    B2 b;
    std::cout << std::boolalpha << (bool)b << std::endl;
}

귀하의 예에서 (bool) b는 B2에 대해 bool 연산자를 호출하려고 시도하고 B2는 bool 연산자를 상속했으며 int 연산자는 지배 규칙에 따라 int 연산자가 호출되고 B2에서 상속 된 bool 연산자가 있습니다. 그러나 B2 클래스 자체에 bool 연산자를 명시 적으로 포함하면 문제가 해결됩니다.


답변

이전 답변 중 일부는 이미 많은 정보를 제공합니다.

내 기여는 “캐스트 작업”이 “오버로드 된 작업”과 유사하게 컴파일된다는 것입니다. 각 작업에 대해 고유 한 식별자를 가진 함수를 만들고 나중에 필요한 연산자 또는 캐스트로 대체하는 것이 좋습니다.

#include <iostream>

class B {
public:
    bool ToBool() const {
        return false;
    }
};

class B2 : public B {
public:
    int ToInt() {
        return 5;
    }
};

int main() {
    B2 b;
    std::cout << std::boolalpha << b.ToBool() << std::endl;
}

그리고 나중에 연산자 또는 캐스트를 적용합니다.

#include <iostream>

class B {
public:
    operator bool() {
        return false;
    }
};

class B2 : public B {
public:
    operator int() {
        return 5;
    }
};

int main() {
    B2 b;
    std::cout << std::boolalpha << (bool)b << std::endl;
}

내 2 센트.


답변