다음의 짧은 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()
필요 int
에 bool
컴파일러는 첫 번째를 선택해야하므로, 변환. 않는 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 typeT
또는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 ()
유형의 상수가 아닌 개체를 사용하여 변환이 요청되는 경우 분명히 더 나은 일치 입니다.B2
operator 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()
있다 bool
에 bool
있는 경우는 제 표준 변환 반면 (신원 변환) operator int ()
인 int
에 bool
부울 변환된다.
따라서 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 센트.