Come sovraccaricare l'operatore C++, ma solo per determinati input?

Come sovraccaricare l'operatore C++, ma solo per determinati input?

Soluzione per Come sovraccaricare l'operatore C++, ma solo per determinati input?
è riportato di seguito:

Ho una classe per la quale vorrei sovraccaricare gli operatori di addizione. Per il mio caso d'uso, ha senso consentire l'aggiunta solo nel caso in cui a la variabile di ogni oggetto è uguale.

Qual è il modo migliore per gestire il caso in cui non lo sono? Lanciare un'eccezione, qualcos'altro?

class A {
private:
    int a, b;

public:
    A(int a, int b)
        :a(a), b(b) {}

    A& operator+=(const A& rhs) {
        if (this->a == rhs.a) {
            this->b += rhs.b;
            return *this;
        }
        else { //this->a != rhs.a
            //what should I put here?
        }
    }
};

.

Modifiche:

Questi oggetti vengono creati in fase di esecuzione durante il file io.

Questi oggetti rappresentano un punto dati in uno spettro. Ha senso aggiungere le intensità di due punti dati solo se si trovano nella stessa posizione.

a è limitato all'intervallo (-180,0, 360,0)

Questo puzza come a è la proprietà del tipo, non una proprietà del valore... Cosa rappresenta esattamente questa classe?

Il modo minimamente praticabile (IMHO) per avvicinarsi a questo è rendere esplicita la transizione da un tipo "va bene qualsiasi cosa" a un "tipo compatibile con un particolare valore di a". Cioè:

MyClass x(1,2), y(1,5);

x += y; // won't compile

x.makeCompatibleWith(y) += y; // will compile

Di solito è una pessimizzazione avere operatori aritmetici come += gettare. Invece, chiedi a qualcos'altro di assumere il costo, quindi il costo è esplicito e puoi mantenere += niente. È anche facile cercare nel progetto operazioni costose (beh, makeCompatibleWith non è super costoso, solo più costoso di += poiché aggiunge il sovraccarico della gestione delle eccezioni).

Supponendo che i casi non validi debbano essere rilevati durante il test, il makeCompatibleWith la funzione potrebbe assert la condizione che richiede, ma nelle build di rilascio restituirebbe un oggetto fittizio che trasforma il += in un no-op poiché non modificherà x – pur mantenendo += molto semplice e veloce.

Quanto a cosa dovrebbe esattamente makeCompatibleWith ritorno:tocca a te. Può essere un tipo che contiene un riferimento, ad esempio:

class MyClass
{
  int a, b;
  struct Internal
  {
    MyClass &val;
    Internal(MyClass &val) : val(val) {}
    MyClass &operator+=(const MyClass &o) noexcept {
      val.b += o.b;
      return val;
    }
    MyClass operator+(const MyClass &o) const noexcept {
      return { val.a, val.b + o.b };
    }
  };
public:
  MyClass() : a{}, b{} {}
  MyClass(int a, int b) : a(a), b(b) {}
  Internal makeCompatibleWith(const MyClass &o) noexcept {
    thread_local static MyClass dummy;
    assert(a == o.a);
    if (a != o.a)
      return { dummy };
    return { *this };
  }
};

Nota che makeCompatibleWith sarebbe un comportamento indefinito se utilizzato da più thread se dummy non era thread-local.