Perché usare "b <a? a: b "anziché" a <b? b: a "per implementare il modello massimo?


154

Modelli C ++ - La guida completa, 2a edizione introduce il modello massimo :

template<typename T>
T max (T a, T b)
{
  // if b < a then yield a else yield b
  return  b < a ? a : b;
}

E spiega l'utilizzo “b < a ? a : b”anziché “a < b ? b : a”:

Si noti che il modello max () secondo [StepanovNotes] restituisce intenzionalmente “b <a? a: b "anziché" a <b? b: a ”per garantire che la funzione si comporti correttamente anche se i due valori sono equivalenti ma non uguali.

Come capire "even if the two values are equivalent but not equal. "? “a < b ? b : a”sembra avere lo stesso risultato per me.


8
Mi sembra sbagliato ... Entrambe le risposte sono "corrette", ma se ae bsono equivalenti , allora !(a < b) && !(b < a)è vero, quindi a < be b < asono entrambe false, quindi in b < a ? a : b, bviene restituito, che non è quello che vuoi ... Tu vuoi a < b ? b : a.
Holt,

1
Spesso puoi distinguere tra equivalente ae bcon std::addressofet. al.
Caleth,

14
Se lo fai a = max(a, b);(ripetutamente) potresti non voler sostituire ainutilmente.
Bo Persson,

2
A proposito, questo modello dovrebbe prendere i parametri dai riferimenti cost e restituirli dai riferimenti const, altrimenti stai facendo un sacco di copie inutili (e sovrascriverai acon una copia di a).
Holt,

3
@Caleth: il tipo canonico che ha sia equivalenza che uguaglianza è CaseInsensitiveString. Per quel tipo, né a <A né A <a. Ma std::addressofè irrilevante. In effetti, per il dato T max(T a, T b)lo sappiamo già addressof(a) != addressof(b).
Salterio,

Risposte:


150

std::max(a, b) è infatti specificato per tornare a quando i due sono equivalenti.

Questo è considerato un errore da Stepanov e altri perché rompe l'utile proprietà che ha dato ae b, puoi sempre ordinarli {min(a, b), max(a, b)}; per questo, vorresti max(a, b)tornare bquando gli argomenti sono equivalenti.


48
Da quel link "È difficile per me incolpare le persone che lo fanno: dopo tutto, seguono semplicemente le specifiche standard C ++ di max scritte da me. Mi ci sono voluti diversi anni per capire che mi sbagliavo." - Wow!
Jack Aidley,

23
non puoi farlo {min(a, b), max(b, a)}?
Captain Man,

12
@CaptainMan: Sì, ma è ancora meno ovvio. Direi che ha senso logico che max(a,b)restituirà un if-and-only-if min(a,b)restituisce b, e viceversa in modo che siano il contrario l'uno dell'altro e l'insieme (non ordinato) {min(a,b), max(a,b)}sia sempre uguale {a,b}.
Jack Aidley,

5
@ jpmc26: se si sta, ad esempio, ordinando un elenco di eventi in base al tempo, non è necessario preoccuparsi se l'operazione di ordinamento è stabile per avere ogni evento che appare esattamente una volta nell'input, allo stesso modo apparire esattamente una volta nell'output. Alcune operazioni (come la ricerca e l'eliminazione di eventi duplicati) possono richiedere l'utilizzo di un ordine completo, ma in molti altri casi può essere accettabile elencare eventi simultanei in ordine arbitrario, ma non duplicarli o ometterli.
supercat,

2
@supercat L'applicazione mine maxqualsiasi cosa tranne il timestamp (la chiave di ordinamento) in quello scenario non ha senso. Gli stessi eventi (gli oggetti) non dovrebbero nemmeno essere comparabili se l'uguaglianza non implica l'intercambiabilità. L'unico modo {min(a, b), max(a, b)}rende alcun senso come un meccanismo tipo è se gli oggetti sono intercambiabili.
jpmc26,

62

Questa risposta spiega perché il codice dato è sbagliato dal punto di vista standard C ++, ma è fuori contesto.

Vedi la risposta di @ TC per una spiegazione contestuale.


Lo standard definisce std::max(a, b)come segue [alg.min.max] (l'enfasi è mia):

template<class T> constexpr const T& max(const T& a, const T& b);

Richiede : il tipo T è LessThanComparable (Tabella 18).

Restituisce : il valore più grande.

Note : restituisce il primo argomento quando gli argomenti sono equivalenti.

Equivalente qui significa che !(a < b) && !(b < a)è true [alg.sorting # 7] .

In particolare, se ae bsono equivalenti, entrambi a < be lo b < asono false, quindi il valore a destra di :verrà restituito nell'operatore condizionale, quindi adeve essere a destra, quindi:

a < b ? b : a

... sembra essere la risposta corretta. Questa è la versione utilizzata da libstdc ++ e libc ++ .

Quindi le informazioni nel tuo preventivo sembrano sbagliate secondo lo standard attuale, ma il contesto in cui è definito potrebbe essere diverso.


4
Link Godbolt che spiega il problema (grazie @songyuanyao per la definizione di X).
Holt,

1
@JackAidley Ho modificato la risposta per specificare che il ragionamento si rivolge allo standard attuale.
Holt,

@codekaizer In realtà mi riferivo a "Se definiamo equiv (a, b) come! comp (a, b) &&! comp (b, a)" . Ho cambiato il link per un preventivo migliore (3 righe di seguito nello standard ...).
Holt,

1
Nessuno sorpreso ha menzionato il virgola mobile, dove a<be b<apossono essere entrambi falsi perché non ordinati (uno o entrambi NaN, quindi ==è anche falso). Ciò potrebbe essere visto come una sorta di equivalenza. vagamente correlato: strumenti di maxsd a, bistruzione di x86 a = max(b,a) = b < a ? a : b. ( Qual è l'istruzione che fornisce FP senza branch min e max su x86? ). L'istruzione mantiene l'operando di origine (il secondo) su non ordinato, quindi un loop su un array ti darà NaN se ci fossero NaN. Ma max_seen = max(max_seen, a[i])ignorerà NaNs.
Peter Cordes,


21

Il punto è quale dovrebbe essere restituito quando sono equivalenti; std::maxdeve tornare a(cioè il primo argomento) per questo caso.

Se sono equivalenti, restituisce a.

Quindi a < b ? b : adovrebbe essere usato; d'altra parte, b < a ? a : b;tornerà in bmodo errato.

(Come ha detto @Holt, la citazione sembra opposta.)

"i due valori sono equivalenti ma non uguali" significa che hanno lo stesso valore quando vengono confrontati, ma possono essere oggetti diversi in altri aspetti.

per esempio

struct X { int a; int b; };
bool operator< (X lhs, X rhs) { return lhs.a < rhs.a; }
X x1 {0, 1};
X x2 {0, 2};
auto x3 = std::max(x1, x2); // it's guaranteed that an X which cantains {0, 1} is returned

1
Potresti per favore approfondire il motivo per cui std::max(a, b)deve tornare a, se ae bsono equivalenti?
眠 り ネ ロ ク

4
@ ネ ロ ク - È solo una scelta arbitraria da parte dello standard . Anche se è un po 'discutibile se è buono.
StoryTeller - Unslander Monica,

Sono solo io o è in contraddizione con la domanda? Se ae bsono equivalenti, allora !(a < b) && !(b < a)è vero, così a < be b < asono falsi, quindi ...?
Holt,

2
@ ネ ロ ク Suppongo che lo standard voglia solo farlo determinare; quando sono equivalenti quale dovrebbe essere restituito.
Songyuanyao,

1
grazie per rinfrescare la mia memoria sul motivo per cui un oggetto sarebbe "equivalente" ma non "uguale".
Jeff Walker,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.