Development Tip

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

yourdevel 2020. 10. 10. 12:06
반응형

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


다음과 같은 짧은 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()const 정규화되지 않았기 operator bool() const때문에 clang over에 의해 선택되는 b반면 bool의 변환 연산자는 그렇습니다.

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

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

두 번째 b는 const 규정이 아니기 때문에 더 나은 일치 입니다.

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

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

우리는 부울 ostream에 삽입 ([ostream.inserters.arithmetic]에 따라,이 표준 : basic_ostream :: 연산자 << (BOOL 브로))의 변환 결과로하는 값이라는 점에 동의하는 경우 bbool우리가 전환 가능 파고 .

1. 캐스트 표현

b를 bool로 캐스트

(bool)b

평가하다

static_cast<bool>(b)

C ++ 11 5.4 / 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 및 산출 유형 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 ()

If both conversion functions share the same const qualification, overload resolution of those function won't do the trick anymore. In this case, conversion (sequence) ranking comes into place.

3. Why is operator bool () selected when both conversion function share the same const qualification?

The conversion from B2 to bool is an user-defined conversion sequence (C++11, 13.3.3.1.2/1 [over.ics.user])

A user-defined conversion sequence consists of an initial standard conversion sequence followed by a userdefined conversion followed by a second standard conversion sequence.

[...] If the user-defined conversion is specified by a conversion function, the initial standard conversion sequence converts the source type to the implicit object parameter of the conversion function.

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

[...] defines a partial ordering of implicit conversion sequences based on the relationships better conversion sequence and better conversion.

[...] User-defined conversion sequence U1 is a better conversion sequence than another user-defined conversion sequence U2 if they contain the same user-defined conversion function or constructor or aggregate initialization and the second standard conversion sequence of U1 is better than the second standard conversion sequence of U2.

The second standard conversion is case of operator bool() is bool to bool (identity conversion) whereas the second standard conversion in case of operator int () is int to bool which is a boolean conversion.

Therefore, the conversion sequence, using operator bool (), is better if both conversion function share the same const qualification.


The C++ bool type has two values - true and false with corresponding values 1 and 0. The inherent confusion can be avoided if you add a bool operator in B2 class that calls base class(B)'s bool operator explicitly, then the output comes as false. Here's my modified program. Then operator bool means operator bool and not operator int by any means.

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

In your example, (bool) b was trying to call the bool operator for B2, B2 has inherited bool operator, and int operator, by dominance rule, int operator gets called and the inherited bool operator in B2. However, by explicitly having a bool operator in B2 class itself, the problem gets solved.


Some of the previous answers, already provide a lot of info.

My contribution is, "cast operations" are compiled similar, to "overloaded operations", I suggest to make a function with a unique identifier for each operation, and later, replace it by the required operator or cast.

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

And, later, apply the operator or cast.

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

Just my 2 cents.

참고URL : https://stackoverflow.com/questions/25241516/different-cast-operator-called-by-different-compilers

반응형