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


80

다음의 짧은 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출력 을 생성 합니다.


3
내가 알기로 B2에는 B :: operator bool ()과 B2 :: operator int ()의 두 가지 연산자가 있습니다. 두 연산자는 모두 const 연산자가 아니며 const와 non-const 버전간에 차이가 있습니다. bool의 const 버전을 B에 추가하고 int의 const 버전을 B2에 추가하고 변경 사항을 볼 수 있습니다.
Tanuki

1
const여기서 핵심입니다. 두 연산자 모두 동일하게 const이거나 아니거나 예상대로 작동하며 bool버전이 "승리"합니다. constness가 다르면 항상 비 const 버전이 모든 플랫폼에서 "승리"합니다. 파생 클래스와 연산자의 다른 constness로 인해 VS는 다른 두 컴파일러와 다르게 작동하므로 버그가있는 것 같습니다.
buc

다른 기본 생성자가 다른 동작을 담당 할 수 있습니까?
ldgorman 2014-08-11

16
EDG도 인쇄합니다 true. GCC, Clang 및 EDG가 모두 동의하고 MSVC가 동의하지 않으면 일반적으로 MSVC가 잘못되었음을 의미합니다.
Jonathan Wakely

1
참고 : 일반적으로 C ++에서 반환 유형은 오버로드 해결에 관여 하지 않습니다 .
Matthieu M.

답변:


51

표준 상태 :

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

§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.


2
C ++ 11 이후의 첫 번째 초안 인 N3337을 얻을 수 있으며 여기 에는 매우 사소한 변경 사항 만 무료로 포함되어 있습니다. 표준의 적절한 부분을 찾는 것은 일반적으로 PDF의 섹션을 판단하고, 사물이 어디에 있는지 (표준을 조회 / 인용하여) 파악하고, CTRL + F로 관련 키워드를 검색하는 문제입니다.
Robert Allan Hennigan Leahy

1
@ikh 현재 C 또는 C ++ 표준 문서는 어디에서 찾을 수 있습니까? 이것이 최선의 질문이며 내가 아는 한 C와 C ++ 모두에서 사용할 수있는 모든 초안을 다룹니다.
Shafik Yaghmour 2014-08-11

2
이것이 내가 "어떤 변환 순서가 더 나은지 결정한 후에 만 ​​적용된다"고 말한 것입니다. 그러나 나는 이것이 대답의 중요한 부분이라고 생각합니다. (이론적으로) 암시 된 객체 인수의 변환과 "반환 유형의 변환"사이에는 충돌이 있으며, 다른 단계의 과부하 해결에 의해 명확하게 해결됩니다. [over.match.best] /1.4는 이러한 단계의 분리와 최종 표준 변환 시퀀스가이 경우에 신경 쓰지 않는 이유를 명확하게 지적합니다.
dyp

2
귀하의 제안을 반영하기 위해 답변을 편집하여 "최상의"변환 연산자를 선택할 때 이러한 변환 연산자의 반환 유형이 고려되지 않는 이유를 설명했습니다.
Robert Allan Hennigan Leahy

2
@RobertAllanHenniganLeahy 그게 질문의 요점 이었기 때문에 답변의 어딘가에 댓글이 아닌 어딘가에 나타나야하지 않나요?
Barmar

9

짧은

변환 함수는 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 자격을 공유하는 경우를 사용하는 변환 시퀀스 가 더 좋습니다.


대부분의 경우 결과로 수행하는 작업이 계산 방식에 영향을주지 않는다는 것은 매우 직관적입니다. 계산 방법 a/b은 코드가 float f = a/b;이거나 int f = a/b;. 그러나 이것은 약간 놀라운 경우입니다.
David Schwartz

1

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 연산자를 명시 적으로 포함하면 문제가 해결됩니다.


8
프로그램을 작성하는 데는 좋은 해결책이지만 이 이상한 일이 발생 하는지 자세히 설명 하지 않습니다 .
ikh aug

4
true and false with corresponding values 1 and 0그것은 지나치게 단순화 된 것입니다. true정수로 변환 1되지만 값이 "가짐"을 의미하지는 않습니다 1. 실제로 아래에 true있을 수 42있습니다.
밝기 경주 궤도에

B2에서 명시적인 bool 연산자를 사용하면 B2, operator int 또는 operator bool에서 호출되는 내용의 혼란과 컴파일러 종속성을 피할 수 있습니다.
Dr. Debasish Jana

컴파일러 종속성이 없습니다. 표준은 0이 거짓으로 변환되고 1이 참으로 변환되도록 요구합니다.
Puppy

1
@LightnessRacesinOrbit 요점은 아마도 : a bool는 0 또는 1의 값을 갖지 않습니다. 취할 수있는 유일한 값은 falseand입니다 true(이 bool정수 유형으로 변환 되면 0과 1 로 변환 됨).
James Kanze 2014-08-11

0

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

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

#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 센트.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.