なぜ「b <a?「a <b?の代わりにa:b」b:a” maxテンプレートを実装するには?


154

C ++テンプレート-完全ガイド、第2版では、maxテンプレートが導入されています。

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

そしてそれは“b < a ? a : b”代わりに使用することを説明します“a < b ? b : a”

[StepanovNotes]によるmax()テンプレートは意図的に「b <a?「a <b?の代わりにa:b」b:a” 2つの値が等しくても等しくない場合でも、関数が正しく動作することを確認します。

even if the two values are equivalent but not equal.」を理解するには?“a < b ? b : a”私には同じ結果があるようです。


8
私には間違っているように見えます...両方の答えは「正しい」ですが、ab同等!(a < b) && !(b < a)ある場合、はtrueでa < bありb < a、両方ともfalseなのでb < a ? a : bbで返されますa < b ? b : a
Holt

1
多くの場合、同等のものabwithを区別できstd::addressofます。al。
Caleth

14
a = max(a, b);(繰り返し)行う場合は、a不必要に交換したくない場合があります。
Bo Persson

2
ところで、このテンプレートはconst-referencesによってパラメータを取り、const-referenceによってそれらを返す必要があります。それ以外の場合は、無用なコピーの束を作成します(そしてaのコピーでオーバーライドしようとしますa)。
Holt

3
@Caleth:等価性と等価性の両方を持つ正規型はCaseInsensitiveStringです。そのタイプの場合、a <AでもA <aでもありません。しかしstd::addressof、無関係です。実際、与えられたT max(T a, T b)私たちはすでに知っていaddressof(a) != addressof(b)ます。
MSalters 2018年

回答:


150

std::max(a, b)実際aには、2つが同等の場合に返されるように指定されています。

それはのミスと考えられていますステパノフそれが与えられたことに有用な性質を壊すので、他ab、あなたは常にソート彼らとすることができます{min(a, b), max(a, b)}。そのため、引数が同等の場合max(a, b)に戻りますb


48
そのリンクから「そうする人々を責めることは私にとって難しいです。結局のところ、彼らは私が書いたmaxの C ++標準仕様に従っているだけです。私が間違っているのを見るのに数年かかりました。」- ワオ!
Jack Aidley

23
あなただけではできません{min(a, b), max(b, a)}か?
キャプテンマン

12
@CaptainMan:はい、しかしそれはまだ明白ではありません。max(a,b)if-and-only-if min(a,b)がbを返し、その逆も同様であり、それらが互いに逆であり、(順序付けられていない)セット{min(a,b), max(a,b)}が常にに等しいことは論理的に理にかなっていると私は主張します{a,b}
Jack Aidley

5
@ jpmc26:たとえば、イベントのリストを時間順に並べ替える場合、並べ替え操作が安定しているかどうかを気にする必要はなく、各イベントが入力に1回だけ出現し、同様に出力に1回だけ出現するようにします。特定の操作(重複したイベントの検索と削除など)では、完全な順序付けを使用する必要がありますが、他の多くの場合、同時イベントを任意の順序でリストすることは許容できますが、複製または省略はできません。
スーパーキャット2018年

2
@supercat そのシナリオでタイムスタンプ(並べ替えキー)以外に適用minmaxても意味がありません。イベント(オブジェクト)自体は、同等性が互換性を意味しない場合は比較できません。唯一の方法は作る任意のオブジェクトが交換されている場合は、ソート・メカニズムがあるとして意味を。{min(a, b), max(a, b)}
jpmc26

62

この回答は、与えられたコードがC ++標準の観点からは間違っている理由を説明していますが、コンテキスト外です。

状況説明については、@ TCの回答を参照してください。


標準はstd::max(a, b)次のように定義します[alg.min.max](強調は私のものです):

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

必要:タイプTはLessThanComparable(表18)です。

戻り値:大きい方の値。

備考引数が等しい場合、最初の引数を返します。

ここで同等とは、それ!(a < b) && !(b < a)true [alg.sorting#7]であることを意味します。

特に、場合ab同等であり、双方a < bb < aしているfalseので、右側の値:ので、条件演算子で返されるaように、右側になければなりません。

a < b ? b : a

...正解のようです。これは、libstdc ++およびlibc ++で使用されるバージョンです

したがって、現在の標準では見積書の情報は間違っているように見えますが、それが定義されているコンテキストは異なる場合があります。


4
問題を説明するGodboltリンク(の定義について@songyuanyaoに感謝しますX)。
Holt

1
@JackAidley推論が現在の標準を対象とするように指定するために回答を編集しました。
Holt

@codekaizer私が実際に言及したのは、「equiv(a、b)を!comp(a、b)&&!comp(b、a)と定義した場合」です。リンクをより適切な引用に変更しました(標準の3行下...)。
Holt

1
誰もが浮動小数点について言及していないことに驚いています。それらは順序付けされていないため、両方ともfalseになる可能性がa<bありb<aます(一方または両方のNaN ==もfalseである)。それは一種の同等性と見ることができます。大まかに関連:x86のmaxsd a, b命令はを実装していa = max(b,a) = b < a ? a : bます。(x86でブランチレスFPの最小値と最大値を与える命令は何ですか?)命令はソースオペランド(2番目のオペランド)を順序付けずに保持するため、配列に対するループは、NaNがあった場合にNaNを提供します。ただし、max_seen = max(max_seen, a[i])NaNは無視されます。
Peter Cordes


21

ポイントは、それらが同等である場合にどれを返すかです。この場合、(つまり最初の引数)std::maxを返すa必要があります。

同等の場合はを返しますa

したがってa < b ? b : a、使用する必要があります。一方、正しくb < a ? a : b;返されbません。

(@Holtが言ったように、引用は反対のようです。)

「2つの値は等しいが等しくない」とは、比較すると同じ値になることを意味しますが、他のいくつかの点では異なるオブジェクトになる場合があります。

例えば

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
同等である場合、なぜstd::max(a, b)を返す必要があるかについて詳しく説明してもらえますか?aab
眠りネロク

4
@ネロク- これは規格の任意の選択です。それが良いかどうかは少し議論の余地がありますが。
StoryTeller-Unslander Monica

それは私だけですか、それとも質問と矛盾していますか?場合ab同等であり、そして!(a < b) && !(b < a)真で、そうa < bb < a...そう、偽ですか?
Holt

2
@ネロク標準はそれを決定させたいだけだと思います。それらが同等である場合、どちらを返すか。
songyuanyao

1
オブジェクトが「同等」ではなく「同等」ではない理由を思い出してくれてありがとう。
ジェフウォーカー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.