2014-04-30 2 views
2

< 연산자를 정의한 후에는 나머지 관계 연산자의 동작을 추정 할 수 있습니다. 내 수업을 위해 그렇게 할 수있는 방법을 구현하려고합니다.C++ 관계 연산자 생성자

내가 원하는 것은 <과 나머지 연산자를 암시 적으로 기본값으로 정의하는 것입니다. 내가 지금까지있어하는 I가 정교 것이 디자인이며, 더 아래 : 연산자의 나머지를 가지고 그냥 relational에서 상속 걸릴 것 < 연산자를 구현하는 클래스에 대한 그래서

template<typename T> 
struct relational 
{ 
    friend bool operator> (T const &lhs, T const &rhs) { return rhs < lhs; } 
    friend bool operator==(T const &lhs, T const &rhs) { return !(lhs < rhs || lhs > rhs); } 
    friend bool operator!=(T const &lhs, T const &rhs) { return !(rhs == lhs); } 
    friend bool operator<=(T const &lhs, T const &rhs) { return !(rhs < lhs); } 
    friend bool operator>=(T const &lhs, T const &rhs) { return !(lhs < rhs); } 
}; 

기본값.

struct foo : relational<foo> 
{ 
    // implement < operator here 
}; 
  1. 어떤 대안, 더 나은 디자인이 있습니까?
  2. 이 코드에는 시한 폭탄이 있습니까? 사용자가 연산자 중 하나에 대한 사용자 정의 구현을 정의하려는 경우 과부하 해결은 템플릿이 아닌 (사용자 정의) 구현을 선택하고 선택하는 것으로 가정합니다. 그 경우가 아니면 (또는 내가 relational에서 상속하는 클래스 템플릿에 문제가있을 것입니다) relational 같은 연산자를 구현해야합니까? 귀하의 조언에 대한

    // inside the relational struct 
    friend bool operator>(relational const &lhs, relational const &rhs) 
    { // functions that involve implicit conversion are less favourable in overload resolution 
         return (T const&)rhs < (T const&)lhs; 
    } 
    

감사합니다, 여기에 demo of the code working

+0

. 이것이 어떤 점에서 당신을 때릴 지 모르겠습니다. –

+3

사이드 노트 : rhs는 "오른쪽"을 의미하고 lhs는 "왼손 쪽"을 의미합니다. 함수 매개 변수의 이름이 반대로 표시됩니다. 또한'operator =='를'operator = '로 정의하는 것은 수학적으로 흥미로울 수 있지만 실제 operator가'operator <'를 두 번 호출하기 때문에 퍼포먼스 친화적이지는 않을 것입니다. =='. – Shahbaz

+4

1. [Boost.Operators] (http://www.boost.org/doc/libs/1_55_0/libs/utility/operators.htm)와 ['std :: rel_ops'] (http : //en.cppreference.com/w/cpp/utility/rel_ops/operator_cmp). 2. 2 개의 기본 연산을 허용하면 더 많은 클라이언트를 찾을 수 있습니다 :'<'와'=='. '<'순서가 합계가 아닌 '! (a b) && a! = b' 인 경우가 있습니다. – Angew

답변

4

내가 보통 내가 이렇게 로버트 마틴에서 배운 트릭을 사용합니다. 내가 템플릿 클래스를 가지고 :

template <typename T> 
class ComparisonOperators 
{ 
protected: 
    ~ComparisonOperators() {} 

public: 
    friend bool operator==(T const& lhs, T const& rhs) 
    { 
     return lhs.compare(rhs) == 0; 
    } 
    friend bool operator!=(T const& lhs, T const& rhs) 
    { 
     return lhs.compare(rhs) != 0; 
    } 
    friend bool operator<(T const& lhs, T const& rhs) 
    { 
     return lhs.compare(rhs) < 0; 
    } 
    friend bool operator<=(T const& lhs, T const& rhs) 
    { 
     return lhs.compare(rhs) <= 0; 
    } 
    friend bool operator>(T const& lhs, T const& rhs) 
    { 
     return lhs.compare(rhs) > 0; 
    } 
    friend bool operator>=(T const& lhs, T const& rhs) 
    { 
     return lhs.compare(rhs) >= 0; 
    } 
}; 
연산자를 필요로 클래스이에서 파생

:

class Toto : public ComparisonOperators<Toto> 
{ 
    // ... 
public: 
    //  returns value < 0, == 0 or >0, according to 
    //  whether this is <, == or > other. 
    int compare(Toto const& other) const; 
}; 

(IT 은 간단한 메타 프로그래밍을 사용하기 때문에 내 구현, 실제로는 조금 더 복잡하다 그 함수가 존재하는 경우, 오히려 compare보다 isEqual를 호출)

편집 :.

그리고 다시 질문하기 : 이것은 기본적으로 여러분이하고있는 일이며, 이런 종류의 일에 대한 표준 관용구입니다. compare과 같은 명명 된 함수를 사용하는 것을 선호하지만 이는 개인적인 취향입니다. 그러나 isEqual을 처리하는 메타 프로그래밍 트릭은 가치가 있습니다. 즉, 동등성 만 지원하는 유형에 동일한 클래스를 사용할 수 있다는 의미입니다. 컴파일러가 인스턴스 생성을 시도 할 때 오류가 발생합니다. operator<=,하지만 누군가가 그것을 사용하지 않으면 컴파일러는 그것을 인스턴스화하려고 시도하지 않습니다. 그리고 종종 isEqualcompare보다 훨씬 효율적으로 구현 될 수 있습니다.

편집 2 : 그것은 가치가 무엇인지에 대한

: 나는 체계적으로 이렇게. 또한 가 ArithmeticOperators MixedTypeArithmeticOperators (위처럼 (+=면에서 예를 들어 + 정의)이 있지만, 기본 클래스 및 T2되는 두 유형, T1으로, 그것은 사업자의 조합을 모두 제공).그리고 STLIteratorOperators은 인터페이스를 구현합니다. (기본적으로는 isEqual 기능을 가진 GoF 반복기)을보다 합리적이고 쉽게 구현할 수 있습니다. 그들은 많은 상용구를 저장합니다.

편집 3 :

그리고 마지막으로 : 난 그냥 내 툴킷의 실제 코드를 보았다. 는 조건부 isEqual을 지원하는 것은 내가 기억도 간단 보다 없습니다 : 템플릿 클래스는 위의 public 멤버가 있습니다

bool isEqual(T const& other) const 
{ 
    return static_cast< T const* >(this)->compare(other) == 0; 
} 

그리고 operator==operator!= 그냥 isEqual를 사용을 더 템플릿 참여를 메타 프로그래밍. 파생 클래스 이 isEqual을 정의하면이 클래스를 숨기고 사용됩니다. 이 아닌 경우이 번호가 사용됩니다.

+0

'STLIteratorOperators'에 대해서'boost :: iterator_facade'는 어떨까요? – Xeo

+0

@Xeo STLIteratorOperators를 쓸 때 주변에 있지 않았습니다. 나는 그것이 동일한 기능을 제공하지만 아마도 약간의 추가 복잡성을 제공 할 것으로 생각한다. (프로젝트에 Boost를 추가하면 빌드 시간이 크게 늘어날 수 있음을 발견했습니다.) –

+0

Ofcourse protected destructor! 만약 기본 클래스에 대한 포인터를 지우면 망가진 파괴를 피하십시오. 이 추가와 확장은 '비교'로 +1하고 받아 들인 대답을 정당화합니다. –

1

친구가 상속되지 않으므로이 아이디어는 작동하지 않습니다.

#define GEN(X) \ 
    friend bool operator> (T const &lhs, T const &rhs) { return rhs < lhs; } \ 
    friend bool operator==(T const &lhs, T const &rhs) { return !(lhs < rhs || lhs > rhs); } \ 
    friend bool operator!=(T const &lhs, T const &rhs) { return !(rhs == lhs); } \ 
    friend bool operator<=(T const &lhs, T const &rhs) { return !(rhs < lhs); } \ 
    friend bool operator>=(T const &lhs, T const &rhs) { return !(lhs < rhs); } 

을 그리고 당신은 사용할 수 있습니다 : : 그러나, 당신은 영리 예를 들어 대신 매크로를 사용할 수 있습니다 당신은 그 친구 관계는 상속되지 않습니다주의해야한다

class Foo 
{ 
... 
GEN(foo) 
}; 
+1

우정은 여기에 액세스 할 수 없기 때문에 클래스 정의에서 비 멤버 함수 인라인을 정의 할 수 있도록하기 때문에 문제가 보이지 않습니다. –

+0

상속을 시도하고 있기 때문에 정확히 인라인되지 않습니다. –

+2

여기에 상속하는 데 불법은 없습니다. 그리고'T :: operator <'가 public이기 때문에 함수는'T'의 친구가 아니더라도 그것을 액세스 할 수 있습니다. 그의 예제에서 연산자가 'friend'로 선언 된 이유는 이것이 클래스 정의에서 비 멤버 함수의 구현을 제공 할 수있는 유일한 방법이기 때문입니다. –

관련 문제