Comparison Operators etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
Comparison Operators etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

19 Kasım 2020 Perşembe

Three-way comparison operator (spaceship operator) - C++20 ile geliyor

Giriş
Şu satırı dahil ederiz.
#include <compare>
Bu operator C++20 ile geliyor ve bir sürü operator yazmaktan yerine tek bir spaceship operator yazılmasına imkan vererek kodu basitleştiriyor. Aslında bu operator Java'daki compareTo ile aynı

Sonuç Olarak Ne Döner
Açıklaması şöyle. Yani bu operator bir  object döner. Bunlar std::weak_ordering, std::partial_ordering, std::strong_ordering olabilir. Eğer bunları metod imzasına yazmak istemiyorsak, auto da dönebiliriz.
The expression a <=> b returns an object that compares <0 if a < b, compares >0 if a > b, and compares ==0 if a and b are equal/equivalent.
1. std::strong_ordering Sonucu
Eğer sonuç olarak std::strong_ordering kullanıyorsak şu değerleri alabilir.
1. std::strong_ordering::less
2. std::strong_ordering::equivalent
3. std::strong_ordering::equal
4. std::strong_ordering::greater
1.1 equivalent vs equal ise
Açıklaması şöyle. Yani std::strong_ordering açısında her ikisi de aynı, her iki parametre equals ise birbirlerinin yerine geçebilirler. Equality implies substitutability. Ancak diğer sonuç tiplerinde equivalence kullanmak gerekir.
They are the same thing, both numerically and conceptually. If a comparison generates strong ordering between the items being compared, equivalence and equality are the same.

The reason there are two words for it is because this is not the same for other kinds of ordering. Weak and partial don't have equality at all; they only provide equivalence.

Equivalence means that two objects can compare equal. Equality implies something stronger; if they compare equal, one can be substituted for the other in any const usage:
Örnek
Elimizde şöyle bir kod olsun. Çıktı olarak "Equivalent ve Equal" alırız
#include <iostream>
#include <compare>
int main()
{
  std::strong_ordering res = (2<=>2);
  if(res == std::strong_ordering::equivalent)
  {
    std::cout<<"Equivalent...."<<std::endl;
  }
  if(res == std::strong_ordering::equal)
  {
    std::cout<<"Equal"<<std::endl;
  }
  return 0;
}
1.2 less ise
Açıklaması şöyle
The <=> is a three-way comparison which implies that you get not just a binary result, but an ordering (in most cases) and if you have an ordering you can express that ordering in terms of any relational operations. 

A quick example, the expression 4 <=> 5 in C++20 will give you back the result std::strong_ordering::less. The std::strong_ordering::less result implies that 4 is not only different from 5 but it is strictly less than that value, this makes applying the operation (4 <=> 5) < 0 correct and exactly accurate to describe our result.
2. std::partial_ordering Sonucu

Örnek
Her bir üyenin teker teker karşılaştırılması için şöyle yaparız. Şöyle yaparız. Burada metod imzasında auto kullanılıyor
auto X::operator<=>(const Y&) =default;
Örnek - std::partial_ordering
Şöylee yaparız
std::partial_ordering operator <=>(const QVariant& l, const QVariant& r) {
    return l.type() == QMetaType:Int? l.toInt() <=> r.toInt()
         : l.type() == QMetaType::Double? l.toDouble() <=> r.toDouble()
         : throw;
}
Örnek - std::partial_ordering
Şöyle yaparız.
class PersonInFamilyTree { // ...
public:
  std::partial_ordering operator<=>(const PersonInFamilyTree& that) const {
    if (this->is_the_same_person_as ( that)) return partial_ordering::equivalent;
    if (this->is_transitive_child_of( that)) return partial_ordering::less;
    if (that. is_transitive_child_of(*this)) return partial_ordering::greater;
    return partial_ordering::unordered;
  }
  // ... other functions, but no other comparisons ...
};
3. std::weak_ordering Sonucu
Açıklaması şöyle
a total ordering, where equality actually only defines an equivalence class. The canonical example here is case-insensitive string comparison – where two objects might be weak_ordering::equivalent but not actually equal (hence the naming change to equivalent).

20 Mart 2019 Çarşamba

Comparison Operators - Equal to

Giriş
İki nesnenin eşitliği için kullanılan metod imzasında her zaman const kullanmaya dikkat etmek lazım.
bool operator ==(const MyClass& u, const MyClass& v)
//               ^^^^^                 ^^^^^
// Note the use of `const`
Böylece temporary nesneler de kullanılabilir. Eğer yapmazsak aşağıdaki gibi unary minus operatörünün döndürdüğü temporary nesne için derleme hatası alırız.
MyClass u = {3.14,-2.56};
MyClass expected = {-u.vx,-u.vy};

if(expected_vec == (-u))    // Derleme hatası
Birden fazla alana göre kontrol
Birden fazla alana göre eşitliği kontrol etmek için std::tie kullanılabilir. Elimizde şöyle bir sınıf olsun.
struct Foo {
    A a;
    B b;
    C c;
    ...

private:
    auto tied() const { return std::tie(a, b, c, ...); }
};
eşittir öperatörü şöyle yazılabilir.
bool operator==(Foo const& rhs) const { return tied() == rhs.tied(); }
Kendi operatör metodumuzu da yazabilirdik.
struct Foo
{
  int a,b,c,d,e,f;
  bool operator==(const Foo& rhs) { 
   return std::tie(a,b,c,d,e,f) == std::tie(rhs.a,rhs.b,rhs.c,rhs.d,rhs.e,rhs.f); 
  }
};
Örnek
Kalıtım varsa operator==() metodu virtual bir metod çağırabilir. Şöyle yaparız
struct Identifier {    
  bool operator==(const Identifier& other) const {
    return isEqual(other); //Burada virtual metodu çağır
  }
private:
  virtual bool isEqual(const Identifier& other) const = 0;
};

// Note: do not derive this class further (less dyncasts may logically fail).
struct UserIdentifier final : public Identifier {
  int userId = 0;
private:
  virtual bool isEqual(const Identifier& other) const override {
    const UserIdentifier *otherUser = dynamic_cast<const UserIdentifier*>(&other);
    return otherUser && otherUser->userId == userId;
  }
};

// Note: do not derive this class further (less dyncasts may logically fail).
struct MachineIdentifier final : public Identifier {
  int machineId = 0;
private:
  virtual bool isEqual(const Identifier& other) const override {
   const MachineIdentifier *otherMachine = dynamic_cast<const MachineIdentifier*>(&other);
   return otherMachine && otherMachine->machineId == machineId;
  }
};
Örnek
Kendi yazdığımız == operator() metodu ile built-in == metodları çakışabilir. Açıklaması şöyle
In the case of a == b and c == d, the built-in candidate operator==(int, int) (see [over.built]/13) competes with the operator== defined as a member of Foo<T>.
Elimizde şöyle bir kod olsun.
template<class T>
class Foo
{
private:
  T m_value;

public:
  Foo();

  Foo(const T& value):
    m_value(value)
  {
  }

  operator T() const {
    return m_value;
  }

  bool operator==(const Foo<T>& other) const {
    return m_value == other.m_value;
  }
};

struct Bar
{
  bool m;

  bool operator==(const Bar& other) const {
    return false;
  }
};
Görmek için şöyle yaparız. Burada problem Foo sınıfının operator T() metoduna sahip olması. Derleyici Foo'yu bool'a çevirip mi karşılaştıracağını yoksa, Foo'nun operator==() metodunu mu çağıracağını bilemiyor.
Foo<bool> a (true);
bool b = false;
if(a == b) {
  // This is ambiguous
}

Foo<int> c (1);
int d = 2;
if(c == d) {
  // This is ambiguous
}

Foo<Bar> e (Bar{true});
Bar f = {false};
if(e == f) {
  // This is not ambiguous. Why?
}