IT

C ++ 컴파일러가 operator == 및 operator! =를 정의하지 않는 이유는 무엇입니까?

lottoking 2020. 3. 21. 10:44
반응형

C ++ 컴파일러가 operator == 및 operator! =를 정의하지 않는 이유는 무엇입니까?


나는 컴파일러가 가능한 한 많은 작업을 수행하게하는 것을 좋아합니다. 간단한 클래스를 작성할 때 컴파일러는 '무료'에 대해 다음을 제공 할 수 있습니다.

  • 기본 (빈) 생성자
  • 복사 생성자
  • 소멸자
  • 할당 연산자 ( operator=)

같은 -하지만 당신에게 비교 연산자 줄 수없는 것 operator==또는 operator!=. 예를 들면 다음과 같습니다.

class foo
{
public:
    std::string str_;
    int n_;
};

foo f1;        // Works
foo f2(f1);    // Works
foo f3;
f3 = f2;       // Works

if (f3 == f2)  // Fails
{ }

if (f3 != f2)  // Fails
{ }

이것에 대한 정당한 이유가 있습니까? 멤버 별 비교를 수행하는 것이 왜 문제가됩니까? 분명히 클래스가 메모리를 할당하면 조심하고 싶지만 간단한 클래스의 경우 컴파일러 가이 작업을 수행 할 수 있습니까?


컴파일러는 포인터 비교 또는 심층 (내부) 비교를 원하는지 알 수 없습니다.

구현하지 않고 프로그래머가 직접 수행하는 것이 더 안전합니다. 그런 다음 원하는 모든 가정을 할 수 있습니다.


컴파일러가 기본 복사 생성자를 제공 할 수 있다면 비슷한 기본값을 제공 할 수 있어야한다는 주장 operator==()은 어느 정도 의미가 있습니다. 이 연산자에 대해 컴파일러에서 생성 한 기본값을 제공하지 않기로 한 이유는 Stroustrup이 "C ++의 디자인 및 진화"(11.4.1 장-복사 제어)의 기본 복사 생성자에 대해 말한 것으로 추측 할 수 있다고 생각합니다. :

필자는 개인적으로 복사 작업이 기본적으로 정의되어 많은 클래스의 객체를 복사하는 것을 금지하는 것이 불행하다고 생각합니다. 그러나 C ++은 기본 할당 및 C에서 복사 생성자를 상속했으며 자주 사용됩니다.

따라서 "C ++에 기본값이없는 이유는 operator==()무엇입니까?"라는 질문 대신 " C ++에 기본 할당 및 사본 생성자가있는 이유는 무엇입니까?"라는 질문이 있었으며, Stroustrup은 C와의 하위 호환성을 위해 이러한 항목을 마지 못해 포함했습니다. (아마도 C ++의 사마귀의 원인 일뿐 만 아니라 C ++의 인기에 대한 주된 이유 일 수도 있습니다).

내 자신의 목적을 위해 내 IDE에서 새 클래스에 사용하는 스 니펫에는 개인 할당 연산자 및 복사 생성자에 대한 선언이 포함되어 있으므로 새 클래스를 생성 할 때 기본 할당 및 복사 작업이 발생하지 않습니다. 선언을 명시 적으로 제거해야합니다 private:컴파일러가 나를 위해 생성 할 수 있기를 원한다면 섹션 에서 해당 작업 중 하나를 수행하십시오.


심지어 C ++ 20, 컴파일러는 여전히 암시 적으로 생성하지 않습니다 operator==당신을 위해

struct foo
{
    std::string str;
    int n;
};

assert(foo{"Anton", 1} == foo{"Anton", 1}); // ill-formed

그러나 명시 적으로 기본값 을 지정할 수 있습니다 ==.

struct foo
{
    std::string str;
    int n;

    // either member form
    bool operator==(foo const&) const = default;
    // ... or friend form
    friend bool operator==(foo const&, foo const&) = default;
};

디폴트은 ==회원이 많다는 않습니다 ==(기본 복사 생성자는 멤버 현명한 복사 건설을 수행하는 것과 같은 방식으로). 새로운 규칙은 사이 예상되는 관계를 제공 ==하고 !=. 예를 들어 위의 선언으로 두 가지를 모두 쓸 수 있습니다.

assert(foo{"Anton", 1} == foo{"Anton", 1}); // ok!
assert(foo{"Anton", 1} != foo{"Anton", 2}); // ok!

이 특정 기능 (디폴트 operator==와 대칭성 ==!=)에서 유래 한 제안 입니다 광범위한 언어 기능의 일부였다 operator<=>.


IMHO, "좋은"이유는 없습니다. 이 디자인 결정에 동의하는 사람들이 너무 많은 이유는 가치 기반 시맨틱의 힘을 마스터하는 법을 배우지 않았기 때문입니다. 사람들은 구현에 원시 포인터를 사용하기 때문에 많은 사용자 정의 복사 생성자, 비교 연산자 및 소멸자를 작성해야합니다.

적절한 스마트 포인터 (예 : std :: shared_ptr)를 사용하는 경우 기본 복사 생성자는 일반적으로 훌륭하며 가상의 기본 비교 연산자의 명백한 구현은 훌륭합니다.


C ++은 C 가하지 않았기 때문에 ==를하지 않았다고 대답했습니다. 여기서 C는 기본 = 만 제공하지만 = =는 없습니다. C는 간단하게 유지하고 싶었다. C 구현 = memcpy; 그러나 패딩으로 인해 memcmp로 ==을 구현할 수 없습니다. 패딩이 초기화되지 않았기 때문에 memcmp는 동일하더라도 서로 다르다고 말합니다. 빈 클래스에도 같은 문제가 있습니다. memcmp는 빈 클래스의 크기가 0이 아니기 때문에 서로 다르다고 말합니다. 그것은 == 구현하는 것은 더 C에서 일부 코드 = 구현을보다 복잡하게되는 것을 위에서 볼 수있는 예를 들어 ,이에 대한합니다. 내가 틀렸다면 정정 해 주셔서 감사합니다.


비디오 에서 STL을 만든 Alex Stepanov는이 문제를 약 13:00에 해결합니다. 요약하자면, 그는 C ++의 진화를 지켜 보면서 다음과 같이 주장합니다.

  • 불행히도 ==와! = 는 암시 적으로 선언되지 않습니다 (그리고 Bjarne은 그에게 동의합니다). 올바른 언어는 그러한 것들을 준비해야합니다 (그는 계속 해서 의미를 깨는 ! = 를 정의 할 수 없어야한다고 제안합니다 == )
  • 이것이 C의 근본 원인 (많은 C ++ 문제) 인 이유는 할당 연산자가 비트 단위 할당으로 암시 적으로 정의되어 있지만 == 에서는 작동하지 않습니다 . 기사 에서 Bjarne Stroustrup의 자세한 설명을 볼 수 있습니다 .
  • 후속 질문에서 왜 회원 비교로 회원이 아니 었습니까? 그는 놀라운 것을 말합니다 .C는 일종의 국내 언어이며 Ritchie를 위해 이러한 것들을 구현하는 사람은 이것을 구현하기가 어렵다고 말했습니다.

그런 다음 (먼) 미래에 ==! = 가 암시 적으로 생성 될 것이라고 말합니다 .


default를 정의 ==할 수는 없지만 일반적으로 스스로 정의해야하는 기본값 !=정의 할 수 있습니다 ==. 이를 위해 다음 작업을 수행해야합니다.

#include <utility>
using namespace std::rel_ops;
...

class FooClass
{
public:
  bool operator== (const FooClass& other) const {
  // ...
  }
};

자세한 내용 http://www.cplusplus.com/reference/std/utility/rel_ops/ 를 참조하십시오.

또한을 정의 operator< 하면를 사용할 때 <=,>,> =에 대한 연산자를 추론 할 수 있습니다 std::rel_ops.

그러나 std::rel_ops예상하지 않은 유형에 대해 비교 연산자를 추론 할 수 있으므로 사용시주의해야합니다 .

관련 연산자를 기본 연산자에서 추론하는 더 바람직한 방법은 boost :: operators 를 사용하는 것 입니다.

boost에 사용되는 접근 방식은 범위의 모든 클래스가 아니라 원하는 클래스의 연산자 사용법을 정의하기 때문에 더 좋습니다.

"+ =",- "-="등에서 "+"를 생성 할 수도 있습니다 ( 여기에서 전체 목록 참조 ).


C ++ 20은 기본 비교 연산자를 쉽게 구현할 수있는 방법을 제공합니다.

cppreference.com의:

class Point {
    int x;
    int y;
public:
    auto operator<=>(const Point&) const = default;
    // ... non-comparison functions ...
};

// compiler implicitly declares operator== and all four relational operators work
Point pt1, pt2;
if (pt1 == pt2) { /*...*/ } // ok, calls implicit Point::operator==
std::set<Point> s; // ok
s.insert(pt1); // ok
if (pt1 <= pt2) { /*...*/ } // ok, makes only a single call to Point::operator<=>

C ++ 0x 에는 기본 함수에 대한 제안 있었으므로 default operator==;이러한 것들을 명시 적으로 만드는 것이 도움이된다는 것을 알게되었습니다.


개념적으로 평등을 정의하는 것은 쉽지 않습니다. POD 데이터의 경우에도 필드가 동일하더라도 다른 주소 (다른 주소)의 객체가 반드시 동일 할 필요는 없다고 주장 할 수 있습니다. 이것은 실제로 운영자의 사용법에 달려 있습니다. 불행히도 컴파일러는 심령이 아니며 그것을 유추 할 수 없습니다.

이 외에도 기본 기능은 발로 자신을 촬영하는 훌륭한 방법입니다. 설명하는 기본값은 기본적으로 POD 구조체와의 호환성을 유지하기위한 것입니다. 그러나 개발자가 기본 구현의 의미를 잊어 버릴 정도로 혼란을 겪습니다.


이것에 대한 정당한 이유가 있습니까? 멤버 별 비교를 수행하는 것이 왜 문제가됩니까?

기능적으로 문제가되지는 않지만 성능 측면에서 기본 구성원 별 비교는 기본 구성원 별 할당 / 복사보다 차선책 일 수 있습니다. 할당 순서와 달리 비교 순서는 첫 번째 불일치 멤버가 나머지를 건너 뛸 수 있음을 나타내므로 성능에 영향을줍니다. 따라서 일반적으로 동일한 멤버가있는 경우 마지막으로 비교하려는 경우 컴파일러는 어떤 멤버가 같은지 더 잘 모릅니다.

verboseDescription비교적 작은 가능한 날씨 설명 세트에서 선택된 긴 문자열이있는 이 예를 고려하십시오 .

class LocalWeatherRecord {
    std::string verboseDescription;
    std::tm date;
    bool operator==(const LocalWeatherRecord& other){
        return date==other.date
            && verboseDescription==other.verboseDescription;
    // The above makes a lot more sense than
     // return verboseDescription==other.verboseDescription
     //     && date==other.date;
    // because some verboseDescriptions are liable to be same/similar
    }
}

(물론 컴파일러는 부작용이 없다는 것을 인식하면 비교 순서를 무시할 권리가 있지만 아마도 더 나은 정보가없는 소스 코드에서 여전히 퀘스트를 취할 것입니다.)


이 질문에 대한 답변은 시간이 지남에 따라 완성 된 상태로 유지됩니다. C ++ 20 이후에는 명령으로 자동 생성 될 수 있습니다. auto operator<=>(const foo&) const = default;

==,! =, <, <=,> 및> =와 같은 모든 연산자를 생성합니다 . 자세한 내용 https://en.cppreference.com/w/cpp/language/default_comparisons 를 참조하십시오.

운영자의 모양 때문에 <=>우주선 운영자라고합니다. 또한 C ++에서 우주선 <=> 연산자가 필요한 이유는 무엇입니까?를 참조하십시오 . .


POD 유형 클래스의 경우 컴파일러가 대신 할 수 있다는 데 동의합니다. 그러나 단순하다고 생각할 수있는 컴파일러는 잘못 될 수 있습니다. 따라서 프로그래머가 그렇게하는 것이 좋습니다.

두 필드가 고유 한 POD 사례가 있었으므로 비교는 결코 사실로 간주되지 않습니다. 그러나 필자는 페이로드와 비교했을 때만 비교했습니다. 컴파일러는 절대 이해하지 못하거나 스스로 알아낼 수 없었습니다.

게다가-그들은 쓰는 데 오래 걸리지 않습니까?!

참고 URL : https://stackoverflow.com/questions/217911/why-dont-c-compilers-define-operator-and-operator

반응형