2009-10-22 17 views
6

고정 소수점 계산을 가정 해 봅시다. 일부 산술을 캡슐화하는 클래스가 있습니다. 나는 산술 연산자의 오버로드에 대한 생각을 좋아하므로 다음과 같이 씁니다.C++ 연산자 오버로드 및 암시 적 변환

class CFixed 
{ 
    CFixed(int ); 
    CFixed(float); 
}; 

CFixed operator* (const CFixed& a, const CFixed& b) 
{ ... } 

모두 작동합니다. 3 * CFixed (0) 및 CFixed (3) * 10.0f를 쓸 수 있습니다. 하지만 이제는 정수형 피연산자로 연산자 *를 구현하는 것이 훨씬 더 효과적이라는 것을 알게되었습니다.

CFixed operator* (const CFixed& a, int b) 
{ ... } 
CFixed operator* (int a, const CFixed& b) 
{ ... } 

그것은 여전히 ​​작동,하지만 지금 CFixed (0) * 10.0f 호출이 int로 플로트를 변환 버전을 오버로드 (나는 CFixed에 플로트를 변환하는 데 예상) : 그래서 나는 그것을 과부하. 물론 float 버전에도 과부하가 걸릴 수 있지만 코드가 조합 적으로 폭발적으로 증가한 것처럼 보입니다. 어떤 해결 방법이 있습니까 (또는 클래스를 잘못 디자인 했습니까?)? int를 사용하여 연산자의 오버로드 된 버전을 호출하도록 컴파일러에 지시 할 수 있습니까?

+2

반면에 단일 매개 변수 (및 내장 된 매개 변수)를 사용하는 생성자는 물론 명시 적으로 선언되어야하며 이는 물론 승격을 방지하지만 버그를 방지합니다. –

+1

또 다른 "명백한"옹호자 =) 암시 적 생성자는 자신이하는 일을 깨닫는 한 도움이됩니다. 함수를 디자인하고 CFixed 인수를 받아들이고 거기에 정수를 전달하는 것이 좋습니다! – SadSido

+0

나는 너를 믿을 수 없다. – atomice

답변

1

int뿐만 아니라 특수 버전을 선택한다고 가정 할 때 한 가지 할 수있는 것은 템플릿 함수로 제공하고 Boost.EnableIf를 사용하여 제거합니다. 피연산자가 정수형이 아닌 경우 사용 가능한 오버로드 집합에서 오버로드됩니다.

#include <cstdio> 
#include <boost/utility/enable_if.hpp> 
#include <boost/type_traits/is_integral.hpp> 

class CFixed 
{ 
public: 
    CFixed(int ) {} 
    CFixed(float) {} 
}; 

CFixed operator* (const CFixed& a, const CFixed& ) 
{ puts("General CFixed * CFixed"); return a; } 

template <class T> 
typename boost::enable_if<boost::is_integral<T>, CFixed>::type operator* (const CFixed& a, T ) 
{ puts("CFixed * [integer type]"); return a; } 

template <class T> 
typename boost::enable_if<boost::is_integral<T>, CFixed>::type operator* (T , const CFixed& b) 
{ puts("[integer type] * CFixed"); return b; } 


int main() 
{ 
    CFixed(0) * 10.0f; 
    5 * CFixed(20.4f); 
    3.2f * CFixed(10); 
    CFixed(1) * 100u; 
} 

당연히, 다른 조건을 사용하여 T = int 인 경우에만 해당 오버로드를 사용할 수있게하십시오. typename boost::enable_if<boost::is_same<T, int>, CFixed>::type ...

클래스를 디자인하는 데있어 템플릿에 더 의존 할 수 있습니다. 예를 들어 생성자가 템플릿 일 수 있으며 다시 필수 유형과 실수 유형을 구별해야하는 경우이 기술을 사용할 수 있어야합니다.

+0

I haven' t는 아직 시도했지만 컴파일러가 템플릿이 아닌 함수 버전을 선호하지 않아야합니까? 나는 당신의 "main"이 "General CFixed"버전을 4 번 호출 할 것이라고 생각합니다 ... – SadSido

+0

... 나는 약간의 오해를하고 있습니다 ... – SadSido

+0

더 나은 일치가 있다면 템플릿을 선호 할 것입니다. enable_if는 조건이 충족되는 경우에만 과부하 후보를 만들고 플로트의 경우에만 첫 번째 과부하 만 선택할 수 있습니다. – UncleBens

0

변환하는 방법은 무엇입니까 explicit?

+0

어떻게 도움이 될까요? 플로트가 연산자 *에 전달되면 포스터는 CFixed (float)를 호출하기를 원했습니다. 명시 적 키워드를 추가하면 연산자가 덜 복잡해집니다. – atomice

3

단 하나의 인수로 호출 할 수있는 생성자가 있으면 암시 적 변환 연산자를 효과적으로 생성 할 수 있습니다. 귀하의 예에서는 CFixed이 필요한 곳이면 intfloat을 모두 전달할 수 있습니다. 컴파일러가 함수의 선언을 포함하는 것을 잊었을 때, 짖지 않고 잘못된 함수를 호출하는 코드를 자동으로 생성 할 수 있기 때문에 이는 물론 위험합니다.

그러므로 좋은 법칙에 따르면 하나의 인수만으로도 호출 할 수있는 생성자를 작성할 때 (이 인수는 두 개의 인수를 취하더라도 하나의 인수로도 호출 할 수 있습니다). 넌 암시 적 변환을 정말로 원하지 않는다면 그 생성자를 explicit으로 만들어야합니다. explicit 생성자는 암시 적 변환을 위해 컴파일러에서 사용되지 않습니다.

당신이 당신의 클래스를 변경해야 할 것입니다 : 나는이 규칙에 매우 몇 가지 예외가 있다는 것을 발견했다

class CFixed 
{ 
    explicit CFixed(int ); 
    explicit CFixed(float); 
}; 

. (std::string::string(const char*) 오히려 유명한 하나입니다.)

편집 : 미안 해요, 나는 int에서 float에 암시 적 변환을 허용하지에 대한 포인트를 놓쳤다.

내가 이것을 방지하기 위해 보는 유일한 방법은 float에 대한 연산자도 제공하는 것입니다.

+1

질문의 요점이 아닙니다. 내 생성자가 명시적일 경우 고정 * 10을 쓸 수 있습니까 ?? – SadSido

+0

@SadSido : 죄송합니다. 편집에 대한 의견을 추가했습니다. – sbi

+0

고마워요! 편집은 훨씬 도움이됩니다. (조금 비관적이지만) – SadSido

0

sbi와 동의한다면, 반드시 단일 매개 변수 생성자를 명시 적으로 만들어야합니다.

당신은 그러나 당신이 템플릿을 작성하는 운영자 <> 기능에서 폭발을 방지 할 수 있습니다

template <class T> 
CFixed operator* (const CFixed& a, T b) 
{ ... } 

template <class T> 
CFixed operator* (T a, const CFixed& b) 
{ ... } 

이 무엇을 코드하는 기능에에 따라, 이것은 단지 당신이에서 변환 지원 유형으로 컴파일됩니다.

+0

템플릿 내부에서 어떤 현상이 발생합니까? :-) Ẹ 똑같이! –

+0

나는 당신이 나를 중간 편집 한 것으로 생각한다. 무슨 일로? – Bill

+0

@Pavel : 사실 Bill이 옳다고 생각합니다. (심지어 실현되지 않았을 수도 있습니다.)). 템플릿을 CFixed operator * (const CFixed & a, T b) {return a * CFixed (b); } OP가 원하는 것을 그렇게해서는 안됩니까? – sbi

4

float 유형으로도 오버로드해야합니다. int에서 사용자 지정 유형 (CFixed)으로의 변환은 기본 제공 부동 소수점 변환보다 float에 우선 순위가 낮습니다.따라서 컴파일러는 과 함께 함수를 추가하지 않는 한 항상 int으로 함수를 선택합니다.

자세한 내용은 C++ 03 표준의 13.3 섹션을 참조하십시오. 고통을 느끼다.

나는 그것도 잊어 버린 것 같습니다. :-(UncleBens은 부동 소수점을 추가하는 것만으로도 double의 버전을 추가해야한다고보고 한 바 있습니다. 그러나 어떤 경우 든 내장형과 관련된 여러 연산자를 추가하는 것은 번거롭지 만 결합을 유발하지는 않습니다 부스트

+0

. ... 그리고 두 번? – UncleBens

+0

@UncleBens,'double'은'float'로 변환 될 것입니다. –

+1

그것은 "모호한 오버로드"라고합니다. 'CFixed (0) * 1.0'을 시도하면, Comeau는 특히'operator * (CFixed, int)'와'operator * (CFixed, float)'가'CFixed * double '의 동일한 후보가된다고 알려줍니다. – UncleBens