C ++에서 구조체를 비교하는 동안 == 연산자가 없습니다.


96

다음 구조체의 두 인스턴스를 비교하면 오류가 발생합니다.

struct MyStruct1 {
    MyStruct1(const MyStruct2 &_my_struct_2, const int _an_int = -1) :
        my_struct_2(_my_struct_2),
        an_int(_an_int)
    {}

    std::string toString() const;

    MyStruct2 my_struct_2;
    int an_int;
};

오류는 다음과 같습니다.

오류 C2678 : 바이너리 '==': 'myproj :: MyStruct1'유형의 왼쪽 피연산자를 사용하는 연산자를 찾을 수 없습니다 (또는 허용되는 변환이 없음).

왜?

답변:


126

C ++에서 structs에는 기본적으로 생성 된 비교 연산자가 없습니다. 직접 작성해야합니다.

bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return /* your comparison code goes here */
}

21
@Jonathan : 왜 C ++는 당신이 당신 struct의 s를 평등 하게 비교하고 싶은지 알고 있습니까? 그리고 당신이 간단한 방법을 원한다면, memcmp당신의 구조체가 포인터를 포함하지 않는 것은 항상 너무 길다.
Xeo 2011-04-21

12
@Xeo : memcmp비 -POD 멤버 (예 :) std::string및 패딩 된 구조에서 실패합니다 .
fredoverflow 2011

16
@Jonathan 내가 아는 "현대적인"언어는 ==연산자를 제공합니다 . (그리고 그들은 그것을 재정의하는 수단을 제공하지 않으므로 결국 멤버 함수를 사용해야합니다). 내가 아는 "현대적인"언어는 가치 의미를 제공하지 않으므로 적절하지 않은 경우에도 포인터를 사용해야합니다.
James Kanze 2011

4
@Jonathan Cases는 주어진 프로그램 내에서도 확실히 다릅니다. 엔티티 객체의 경우 Java에서 제공하는 솔루션이 매우 잘 작동합니다 (물론 C ++에서도 똑같은 작업을 수행 할 수 있습니다. 심지어 엔티티 객체에 대한 관용적 C ++이기도합니다). 문제는 가치 객체에 대해 무엇을해야 하는가입니다. C ++는 operator=C 호환성을 이유로 기본값을 제공합니다 (종종 잘못된 작업을 수행하더라도). operator==그러나 C 호환성에는 . 전 세계적으로 Java가 수행하는 작업보다 C ++가 수행하는 작업을 선호합니다. (저는 C #을 모르기 때문에 더 낫습니다.)
James Kanze 2011-04-25

9
적어도 그것은 해야 가능한 일 = default이!
user362515

94

C ++ 20은 기본 비교, 일명 "우주선"을 도입했습니다operator<=> .이를 통해 컴파일러 생성 </ <=/ ==/ !=/ >=/ 및 / 또는 >명백한 / 순진한 (?) 구현으로 연산자 를 요청할 수 있습니다 .

auto operator<=>(const MyClass&) const = default;

...하지만 더 복잡한 상황에 맞게 사용자 정의 할 수 있습니다 (아래에서 설명). 타당성과 논의가 포함 된 언어 제안 은 여기 를 참조 하십시오 . 이 답변은 C ++ 17 및 이전 버전과 관련이 있으며 구현을 사용자 정의해야하는시기에 대한 통찰력을 제공합니다.operator<=> ... .

이전에 표준화하지 않은 것은 C ++에 약간 도움이되지 않을 수 있지만, 종종 구조체 / 클래스에는 비교 에서 제외 할 데이터 멤버 (예 : 카운터, 캐시 된 결과, 컨테이너 용량, 마지막 작업 성공 / 오류 코드, 커서)가 있습니다. 다음을 포함하되 이에 국한되지 않는 무수한 사항에 대한 결정을 내릴 수 있습니다.

  • 먼저 비교할 필드, 예를 들어 특정 int구성원을 비교하면 동일하지 않은 개체의 99 %를 매우 빠르게 제거 할 수있는 반면map<string,string> 멤버는 종종 동일한 항목을 가지고 있고 비교하는 데 상대적으로 비용이 많이들 수 있습니다. 값이 런타임에로드되면 프로그래머는 다음과 같은 통찰력을 가질 수 있습니다. 컴파일러는 가능하지 않다
  • 문자열 비교 : 대소 문자 구분, 공백 및 구분 기호의 동등성, 이스케이프 규칙 ...
  • float / double을 비교할 때 정밀도
  • NaN 부동 소수점 값이 동일한 것으로 간주되어야하는지 여부
  • 포인터 또는 지적 된 데이터 비교 (후자의 경우 포인터가 배열에 대한 것인지 비교가 필요한 객체 / 바이트 수를 아는 방법)
  • 정렬되지 않은 컨테이너를 비교할 때 순서가 중요한지 (예 : vector, list), 그렇다면 비교하기 전에 제자리에서 정렬하는 것이 좋은지 비교가 완료 될 때마다 임시를 정렬하기 위해 추가 메모리를 사용하는 것이 좋은지 여부
  • 현재 비교해야 할 유효한 값을 보유하고있는 배열 요소의 수 (어딘가에 크기 또는 센티넬이 있습니까?)
  • union비교할의 멤버
  • 정규화 : 예를 들어 날짜 유형은 범위를 벗어난 날짜 또는 월을 허용 할 수 있습니다. 또는 합리적 / 분수 객체는 6/8을 가질 수 있고 다른 하나는 3 / 4ers를 가질 수 있으며 이는 성능상의 이유로 수정합니다. 별도의 정규화 단계로 느리게; 비교하기 전에 정규화를 트리거할지 여부를 결정해야 할 수 있습니다.
  • 약한 포인터가 유효하지 않을 때해야 할 일
  • 구현하지 않는 회원들과 기지 처리하는 방법을 operator==스스로 (하지만있을 수 있습니다 compare()또는 operator<또는 str()또는 게터를 ...)
  • 다른 스레드가 업데이트하려는 데이터를 읽고 / 비교하는 동안 수행해야하는 잠금

따라서 특정 구조에 대해 비교가 의미하는 바에 대해 명시 적으로 생각할 때까지 오류가 발생하는 것이 좋습니다. 컴파일하는 대신 런타임에 의미있는 결과를 제공하지 않습니다 .

즉, bool operator==() const = default;"순진한"멤버 별 ==테스트 괜찮다고 결정할 때 C ++에서 말할 수 있다면 좋을 것 입니다. 동일에 대한 !=. 을 감안할 때 여러 회원 / 기지, "기본" <, <=, >, 및 >=구현은 희망하지만 보인다 - 선언의 수 있지만, 원에 의해 그룹화, 염기는 회원 전에 반드시 인 (회원 주문에 대한 명령형 충돌 주어진 무슨 일이 매우 가능성의 순서에 근거하여 계단식 의존적 사용 전 접근성, 건설 / 파괴). 보다 광범위하게 유용하게 사용하려면 C ++는 선택을 안내하는 새로운 데이터 멤버 /베이스 주석 시스템이 필요합니다.하지만 AST 기반 사용자 정의 코드 생성과 이상적으로 결합 된 표준에 포함되어 있으면 좋을 것입니다. 그것'

평등 연산자의 일반적인 구현

그럴듯한 구현

그것은의 가능성이 합리적이고 효율적으로 구현 될 것이라고 :

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return lhs.my_struct2 == rhs.my_struct2 &&
           lhs.an_int     == rhs.an_int;
}

이것이 필요합니다 operator==위해 MyStruct2너무.

이 구현의 의미와 대안은 아래 MyStruct1의 세부 사항에 대한 토론 제목 아래에 설명되어 있습니다.

==, <,> <= 등에 대한 일관된 접근 방식

std::tuple의 비교 연산자 를 활용 하여 자신의 클래스 인스턴스를 비교하는 것은 쉽습니다 std::tie. 원하는 비교 순서로 필드에 대한 참조 튜플을 만드는 데 사용 하면됩니다. 여기 에서 내 예를 일반화 :

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return std::tie(lhs.my_struct2, lhs.an_int) ==
           std::tie(rhs.my_struct2, rhs.an_int);
}

inline bool operator<(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return std::tie(lhs.my_struct2, lhs.an_int) <
           std::tie(rhs.my_struct2, rhs.an_int);
}

// ...etc...

비교할 클래스를 "소유"(즉, 기업 및 타사 라이브러리가있는 요소를 편집 할 수 있음) 할 때 특히 C ++ 14가 return명령문 에서 함수 반환 유형을 추론 할 준비가되어 있으면 "를 추가하는 것이 더 좋습니다. "멤버 함수를 비교할 수있는 클래스에 연결합니다.

auto tie() const { return std::tie(my_struct1, an_int); }

그런 다음 위의 비교는 다음과 같이 단순화됩니다.

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return lhs.tie() == rhs.tie();
}

보다 완전한 비교 연산자 세트를 원한다면 부스트 연산자 (검색)를 제안 less_than_comparable합니다. 어떤 이유로 부적합한 경우 지원 매크로 (온라인) 아이디어가 마음에들 수도 있고 그렇지 않을 수도 있습니다 .

#define TIED_OP(STRUCT, OP, GET_FIELDS) \
    inline bool operator OP(const STRUCT& lhs, const STRUCT& rhs) \
    { \
        return std::tie(GET_FIELDS(lhs)) OP std::tie(GET_FIELDS(rhs)); \
    }

#define TIED_COMPARISONS(STRUCT, GET_FIELDS) \
    TIED_OP(STRUCT, ==, GET_FIELDS) \
    TIED_OP(STRUCT, !=, GET_FIELDS) \
    TIED_OP(STRUCT, <, GET_FIELDS) \
    TIED_OP(STRUCT, <=, GET_FIELDS) \
    TIED_OP(STRUCT, >=, GET_FIELDS) \
    TIED_OP(STRUCT, >, GET_FIELDS)

... 그런 다음 사용할 수 있습니다 ...

#define MY_STRUCT_FIELDS(X) X.my_struct2, X.an_int
TIED_COMPARISONS(MyStruct1, MY_STRUCT_FIELDS)

(C ++ 14 멤버 타이 버전은 여기 )

MyStruct1의 세부 사항에 대한 토론

독립형 대 멤버를 제공하는 선택에 영향을 미칩니다 operator==()...

독립 구현

당신은 흥미로운 결정을 내릴 수 있습니다. 클래스를에서 암시 적으로 구성 할 수 있으므로 MyStruct2독립 / 비 멤버 bool operator==(const MyStruct2& lhs, const MyStruct2& rhs)함수가 지원합니다 ...

my_MyStruct2 == my_MyStruct1

... 먼저에서 임시 파일 MyStruct1my_myStruct2만든 다음 비교를 수행합니다. 이것은 MyStruct1::an_int생성자의 기본 매개 변수 값인 -1. an_int의 구현에 비교 를 포함하는지 여부에 따라 operator==a MyStruct1는 의 멤버와 MyStruct2동일하게 비교 되는 a와 같을 수도 있고 그렇지 않을 수도 있습니다 ! 또한 임시 생성 은 기존 멤버를 임시로 복사 하고 비교 후에 버려야하기 때문에 매우 비효율적 인 작업 일 수 있습니다 . (물론, 당신은 그 생성자를 만들 거나의 기본값을 제거함으로써 비교를위한 s의 암시 적 생성을 막을 수 있습니다.)MyStruct1my_struct_2MyStruct1my_struct2MyStruct1explicitan_int

회원 구현

당신이 암시 건설 피하려면 MyStruct1A로부터를 MyStruct2, 비교 연산자 A는 멤버 함수를합니다

struct MyStruct1
{
    ...
    bool operator==(const MyStruct1& rhs) const
    {
        return tie() == rhs.tie(); // or another approach as above
    }
};

메모 const만을 멤버 구현에 필요한 - - 키워드를 객체를 비교하는을 수정하지 않는 컴파일러 조언 때문에 허용 될 수 const객체.

가시적 표현 비교

원하는 유형의 비교를 얻는 가장 쉬운 방법은 다음과 같습니다.

    return lhs.to_string() == rhs.to_string();

... 그것은 종종 매우 비싸다-그것들 string은 단지 버려지기 위해 고통스럽게 만들어졌습니다! 부동 소수점 값이있는 유형의 경우 표시되는 표현을 비교하면 표시된 자릿수가 비교 중에 거의 동일한 값이 동일하게 처리되는 허용 오차를 결정합니다.


실제로 비교 연산자 <,>, <=,> =의 경우 <를 구현하는 데만 필요합니다. 나머지는 다음과 같으며 자동으로 생성 될 수있는 구현과는 다른 의미로 구현하는 의미있는 방법이 없습니다. 모든 것을 직접 구현해야한다는 것은 기이합니다.
André

@ 안드레 : 종종 수동 기록 int cmp(x, y)또는 compare함수가 음의 값을 반환 x < y어떤지, 0에 대한 양의 값 x > y의 기초로서 사용되어 <, >, <=, >=, ==, 및 !=; CRTP를 사용하여 모든 연산자를 클래스에 삽입하는 것은 매우 쉽습니다. 이전 답변에 구현을 게시했지만 빨리 찾을 수 없었습니다.
Tony Delroy

@TonyD 물론 당신은 그렇게 할 수 있지만, 그냥 쉽게 구현하는 것입니다 >, <=그리고 >=측면에서 <. 당신은 또한 구현 ==!=그 방법,하지만 일반적으로 생각 엔 매우 효율적인 구현하지 않을 것입니다. 이 모든 작업에 CRTP 또는 기타 트릭이 필요하지 않으면 좋지만 표준은 사용자가 명시 적으로 정의하지 않고 정의 된 경우 이러한 연산자의 자동 생성을 요구 <합니다.
André

앙드레 @ : 그것은 때문이다 ==하고 !=효율적으로 사용하여 표현되지 않을 수 있습니다 <모든 것이 일반적입니다에 대한 비교하여 해당. "더 CRTP 또는 다른 트릭이 필요하지한다면 그것은 좋은 일 것" - 아마도,하지만 CRTP 쉽게 다른 사업자의 많은 (생성하는 데 사용할 수 있습니다 예를 들어, 비트 |, &, ^에서 |=, &=그리고 ^=, + - * / %자신의 할당 형태의 이진 -단항 부정에서와 +)-이 테마에 대한 잠재적으로 유용한 변형이 너무 많아서 하나의 꽤 임의적 인 조각에 대한 언어 기능을 제공하는 것만으로는 특별히 우아하지 않습니다.
Tony Delroy

여러 멤버를 비교 하는 데 사용하는 버전을 그럴듯한 구현에 추가해 주시겠습니까 std::tie?
NathanOliver

17

operator ==대해 명시 적으로 정의해야합니다 MyStruct1.

struct MyStruct1 {
  bool operator == (const MyStruct1 &rhs) const
  { /* your logic for comparision between "*this" and "rhs" */ }
};

이제 == 비교는 이러한 객체 2 개에 대해 합법적입니다.


11

C ++ 20에서 시작, 기본 비교 연산자 (의 전체 세트를 추가 할 수 있어야한다 ==, <=선언하여 클래스 등) 기본 세 방향 비교 연산자를 다음과 같이 ( "우주선"연산자) :

struct Point {
    int x;
    int y;
    auto operator<=>(const Point&) const = default;
};

호환 C ++ 20 컴파일러를 사용하면 MyStruct2의 정의가 호환된다는 가정하에 MyStruct1 및 MyStruct2에 해당 행을 추가하는 것만으로 동등성 비교를 허용 할 수 있습니다.


2

C 또는 C ++의 구조체에서는 비교가 작동하지 않습니다. 대신 필드로 비교하십시오.


2

기본적으로 구조체에는 ==연산자 가 없습니다 . 자체 구현을 작성해야합니다.

bool MyStruct1::operator==(const MyStruct1 &other) const {
    ...  // Compare the values, and return a bool result.
  }

0

기본적으로 == 연산자는 기본 요소에 대해서만 작동합니다. 코드가 작동하도록하려면 구조체에 대한 == 연산자를 오버로드해야합니다.


0

구조체에 대한 비교 연산자를 작성하지 않았기 때문입니다. 컴파일러는이를 생성하지 않으므로 비교를 원하면 직접 작성해야합니다.

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