C ++の論理XOR演算子?


292

そんなことありますか?それが実際に必要になったのは初めてですが、Stroustrupにリストされているものはありません。私は書くつもりです:

// Detect when exactly one of A,B is equal to five.
return (A==5) ^^ (B==5);

しかし、^^演算子はありません。^ここでビット単位を使用して正しい答えを得ることができますか(trueとfalseのマシン表現に関係なく)?私はミックス決して&および&&、または|そして||私はそれを行うことを躊躇して、^^^

bool XOR(bool,bool)代わりに自分の関数を書くほうが快適です。


57
実際、ジム、例えば&と&&の違いはそれだけではありません... 1 && 2はTrueです。しかし、1&2 =>0。そのため、「短絡」は、たまたま持っている特性だと思います。論理はより重要な機能です...
ブライアン・ポストウ

6
しないように言及している2 && 3 == trueの場合、しかし、2&3 == 2
デヴィッド・ソーンリー

1
David Thomley:ええ、ええ、でも2 ==> true、それでいいので...本当にブール値はありません...
Brian Postow

13
@BrianPostow:実際、C ++にはあります。
AdrianWillenbücher2013

7
以下に投稿するように、なぜそれが存在しないのかについてのデニス・リッチーの回答です:c-faq.com/misc/xor.dmr.html
Tobia

回答:


536

!=オペレータはこの目的に役立つbool値。


7
しかしfalse!= false => false
ジョナス

13
これはブール値に対してのみ機能することに注意してください。そして^はそこで完全にうまく機能します。2!= 1 => 1これはあなたが望むものではありません!LiraNunaが言うように、!両側の前でその問題を解決します。しかし、再び、あなたは^ ...ビット単位を使用することができます
ブライアンPostow

8
そうです、 "for boolvalues" については、ブール値以外の場合に必ずしも必要なことを行うとは限らないため、慎重に言及しました。これはC ++であるため、その目的でbool使用するint必要のない実際の型が存在します。
グレッグヒューギル

29
あなたはタイプのためにそれをしたい場合はa、単に書き込み!(a) != !(a)
クリス・ルッツ

6
@ChrisLutz:はい、ただしオーバーロードされた演算子に注意してください。
rwong 2015年

246

真の論理XOR演算の場合、これは機能します。

if(!A != !B) {
    // code here
}

注意!2つの等しくない正の整数(それぞれaがそうすることを、そこにブール値に変換し、それらを否定しているtrue)と評価されてしまうからですfalse


6
AとBが!で否定されている理由がわかりません。
Martin Hansen

26
主にそれらをブール値に変換します。!!それらは異なる必要があるため、それらを否定しても害はありません。
LiraNuna 2013

4
問題は、コンパイラがこれを適切に最適化できるかどうかです。
アインポクルム2014年

6
ブール値を正規化することの重要性を知らなかったので、2日かかりました。
Makan Tayebi 2016

9
@LiraNuna、「AとBが!で否定されている理由」/「主にブール値に変換するため。」-これは答えで言及する価値があると思います。
cp.engr

42

適切な手動論理 XOR実装は、XORを使用して他の論理演算子(||および&&)の一般的な動作をどれだけ厳密に模倣したいかによって異なります。これらの演算子には2つの重要な点があります。1)短絡評価を保証する、2)シーケンスポイントを導入する、3)オペランドを1回だけ評価する。

ご存知のように、XOR評価は、結果が常に両方のオペランドに依存するため、短絡することはできません。したがって、1は問題外です。しかし、2はどうでしょうか。2を気にしない場合、正規化された(つまりbool)値の演算子!=は結果に関してXORの役割を果たします。また、!必要に応じて、オペランドをunary で簡単に正規化できます。したがって!A != !B、その点で適切なXORを実装します。

ただし、余分なシーケンスポイントを気にする場合は、XORを実装する適切な方法!=もビット単位も^適切ではありません。XOR(a、b)を正しく実行する1つの方法は、次のようになります。

a ? !b : b

これは実際に、とに「類似した」自家製XORを作成するのと同じくらい近く||なり&&ます。もちろん、これはXORをマクロとして実装する場合にのみ機能します。シーケンスは関数の引数に適用されないため、関数は機能しません。

誰かが、けれども言う各時系列点を有する唯一の理由はその可能性がある&&||短絡評価を支援することであるので、XORは1を必要としません。これは理にかなっています。ただし、シーケンスポイントが中央にあるXORを検討することは検討に値します。たとえば、次の式

++x > 1 && x < 5

C / C ++での動作と特定の結果を定義しました(少なくともシーケンスに関して)。したがって、次のように、ユーザー定義の論理 XOR から同じことを合理的に期待できます。

XOR(++x > 1, x < 5)

一方、!=ベースのXORにはこのプロパティがありません。


1
||and についての他の重要なことを逃しています&&:C)それらはブールコンテキストでオペランドを評価します。つまり、0とは1 && 2異なり、true 1 & 2です。同様に、^^演算子は、ブールコンテキストでオペランドを評価するこの追加機能を提供するのに役立ちます。たとえば、1 ^^ 2(とは異なり1 ^ 2)false です。
クレイグマックイーン

2
@クレイグ・マックイーン:見逃していない 私の投稿の2番目の段落はそれについて言及しています。私の意見では、オペランドをブール値として扱うことは、論理演算子の重要な機能ではなく、その理由だけでは導入されないという意味です。それらが導入された主な理由は、短絡評価とそれに必要なシーケンスポイントです。
AnT

今日、あなたの提案はまだマクロでしか機能しませんか?関数で評価されるパラメーターの順序がコンパイラーに依存していることは事実ですが、現在、左から右に異なることはまれではありませんか?また、実装でのよう#define XOR(ll,rr) { ll ? !rr : rr }に見える場合、呼び出しのようなint x = 2; XOR(++x > 1, x < 5);場合に誤った結果が返されることをここでコメントに書き留めておく価値があります。int x = 2; XOR( (++x > 1), (x < 5) );正しい期待される結果を得るには、のように、呼び出しに括弧を追加する必要があります。
RA 2017年

1
XORは短絡できないため、シーケンスポイントは必要ありません。その点では、XORは+に似ています。(++ x)+ xが2x + 1と等しいことも主張したくない限り、シーケンスポイントは妥当ではありません。
hkBst 2017

1
@hkBst:これは私の回答の2番目の部分で完全にカバーされていると思います。
AnT 2017

25

XORを実行する別の方法があります。

bool XOR(bool a, bool b)
{
    return (a + b) % 2;
}

これは明らかに次の方法で機能することを実証できます。

#include <iostream>

bool XOR(bool a, bool b)
{
    return (a + b) % 2;
}

int main()
{
    using namespace std;
    cout << "XOR(true, true):\t" << XOR(true, true) << endl
         << "XOR(true, false):\t" << XOR(true, false) << endl
         << "XOR(false, true):\t" << XOR(false, true) << endl
         << "XOR(false, false):\t" << XOR(false, false) << endl
         << "XOR(0, 0):\t\t" << XOR(0, 0) << endl
         << "XOR(1, 0):\t\t" << XOR(1, 0) << endl
         << "XOR(5, 0):\t\t" << XOR(5, 0) << endl
         << "XOR(20, 0):\t\t" << XOR(20, 0) << endl
         << "XOR(6, 6):\t\t" << XOR(5, 5) << endl
         << "XOR(5, 6):\t\t" << XOR(5, 6) << endl
         << "XOR(1, 1):\t\t" << XOR(1, 1) << endl;
    return 0;
}

7
このアプローチでは、かなり遅いコードが生成される可能性があります-libstdc ++を使用して、clangとgccの両方で(!a)!=(!b)よりも3〜5倍遅くなります:quick-bench.com/xtv2StFkR8PCkV4fOiqSgeG1T4Q
xaxxon

18

XOR演算子は短絡できません。つまり、左側のオペランドを評価するだけでは、XOR式の結果を予測することはできません。したがって、^^バージョンを提供する理由はありません。


25
-1は&&と&の主な違いは単なる短絡ではないためです。1 && 2はTrueですが、1&2はFalseです。短絡は単なる便利な副作用です。
Brian Postow、

6
答えは話していません&&し、&まったく。その理由は紹介する理由がないということです^^^^null以外の値を1実際には有用ではないと見なすプロパティは、疑わしいと思います。または、少なくとも私はどんな用途も見ることができません。
ヨハネスシャウブ-litb 2009年

6
また、C ++!= other-languages。CとC ++では、上記のように、短絡はあるべきものだけでなく、根本的に重要です。:)
Johannes Schaub-litb

13
次に、それが存在しない理由に対するDennis Ritchieの回答を読んでください:it.usyd.edu.au/~dasymond/mirror/c-faq/misc/xor.dmr.html
Johannes Schaub-litb

5
なぜそれが存在しないのかについてのDennis Ritchieの回答に関する実用的なリンクは次のとおり
Tobia

13

!a!=!bよりも問題を解決する優れたコードが投稿されました

MSVC 2010で機能するように、BOOL_DETAIL_OPEN / CLOSEを追加する必要があることに注意してください。

/* From: http://groups.google.com/group/comp.std.c++/msg/2ff60fa87e8b6aeb

   Proposed code    left-to-right?  sequence point?  bool args?  bool result?  ICE result?  Singular 'b'?
   --------------   --------------  ---------------  ---------- ------------  -----------  -------------
   a ^ b                  no              no             no          no           yes          yes
   a != b                 no              no             no          no           yes          yes
   (!a)!=(!b)             no              no             no          no           yes          yes
   my_xor_func(a,b)       no              no             yes         yes          no           yes
   a ? !b : b             yes             yes            no          no           yes          no
   a ? !b : !!b           yes             yes            no          no           yes          no
   [* see below]          yes             yes            yes         yes          yes          no
   (( a bool_xor b ))     yes             yes            yes         yes          yes          yes

   [* = a ? !static_cast<bool>(b) : static_cast<bool>(b)]

   But what is this funny "(( a bool_xor b ))"? Well, you can create some
   macros that allow you such a strange syntax. Note that the
   double-brackets are part of the syntax and cannot be removed! The set of
   three macros (plus two internal helper macros) also provides bool_and
   and bool_or. That given, what is it good for? We have && and || already,
   why do we need such a stupid syntax? Well, && and || can't guarantee
   that the arguments are converted to bool and that you get a bool result.
     Think "operator overloads". Here's how the macros look like:

   Note: BOOL_DETAIL_OPEN/CLOSE added to make it work on MSVC 2010
  */

#define BOOL_DETAIL_AND_HELPER(x) static_cast<bool>(x):false
#define BOOL_DETAIL_XOR_HELPER(x) !static_cast<bool>(x):static_cast<bool>(x)

#define BOOL_DETAIL_OPEN (
#define BOOL_DETAIL_CLOSE )

#define bool_and BOOL_DETAIL_CLOSE ? BOOL_DETAIL_AND_HELPER BOOL_DETAIL_OPEN
#define bool_or BOOL_DETAIL_CLOSE ? true:static_cast<bool> BOOL_DETAIL_OPEN
#define bool_xor BOOL_DETAIL_CLOSE ? BOOL_DETAIL_XOR_HELPER BOOL_DETAIL_OPEN

6

シンプルなものを使用してください:

return ((op1 ? 1 : 0) ^ (op2 ? 1 : 0));

5

これは、C ++でXOR比較を作成する方法です。

bool a = true;   // Test by changing to true or false
bool b = false;  // Test by changing to true or false
if (a == !b)     // THIS IS YOUR XOR comparison
{
    // do whatever
}

証明

XOR TABLE
 a   b  XOR
--- --- ---
 T   T   F
 T   F   T
 F   T   T
 F   F   F

a == !b TABLE
 a   b  !b  a == !b
--- --- --- -------
 T   T   F     F
 T   F   T     T
 F   T   F     T
 F   F   T     F

証拠は、入力と出力を徹底的に調査すると、2つのテーブルで、すべての入力セットの結果が2つのテーブルで常に同じであることを示しています。

したがって、元の質問はどのように書くかです:

return (A==5) ^^ (B==5)

答えは

return (A==5) == !(B==5);

または、必要に応じて、

return !(A==5) == (B==5);

!a!=!bは、引数をブール値に変換するため、より良いようです。
xaxxon

3

(A || B) && !(A && B)

最初の部分はA OR Bで、これは包含的ORです。2番目の部分は、AとBではなく、AまたはBですが、AとBの両方ではありません。

これにより、以下の真理値表で証明されたXORが提供されます。

|-----|-----|-----------|
|  A  |  B  |  A XOR B  |
|-----|-----|-----------|
|  T  |  T  |   False   |
|-----|-----|-----------|
|  T  |  F  |   True    |
|-----|-----|-----------|
|  F  |  T  |   True    |
|-----|-----|-----------|
|  F  |  F  |   False   |
|-----|-----|-----------|

私はこのアプローチでパフォーマンスを掘り下げていません:quick-bench.com/PgNgGN8ATrKt7el1dAaJj7QtuF4
xaxxon

それについて防御する必要はありません。
xaxxon

1
@xaxxon:このベンチマークで要点がわからない!?StringCreationでは文字列の作成に使用しましたが、2番目のベンチマークでは使用しませんでした。両方のベンチマークに同一のコードを配置し、各ベンチマーク(XOR、およびXOR2)ごとに異なるXORを呼び出すと、同じベンチマーク結果が得られます。それで、あなたは何を言おうとしてきたのですか?
SoLaR 2018

1

私は "xor"を使用しています(キーワードのようです。Code :: Blocksでは少なくとも太字になります)。&&代わりに "and"と "or"を使用できます||

if (first xor second)...

はい、それはビット単位です。ごめんなさい。


2
私はそれらがどこかに隠されていると思います#definesどこかから。「and」と「xor」はansi Cのキーワードではないと確信しています...(少なくともC79ではない)
Brian Postow

1
@Brian Postow:C79が何なのかはわかりませんが、C ++ 98 andxor標準ライブラリマクロです。それらは「どこか」からではなく、<iso646.h>からのものです。これらのマクロはC99にもあります(C89 / 90については不明)。
AnT、2009年

5
@Brian Postowは:... xorの略で、ビット単位ながら、しかし排他的論理和andである論理と。
AnT

1
C89をC79と誤って入力しました...そして、XORなどは私のK&Rのコピーにはありません。私は今までiso686.hを使用したことはないと思います。少なくとも気づいていないので...そうです、そうです、それらはどこかからの#definesであり、たまたまそのどこかがB-)であることがわかります
Brian Postow

2
それらは確かにそのヘッダーを使用してC99にあります。C ++では、これらは「代替トークン」として言語に統合されてstruct A { compl A() { } };おり、たとえば、デストラクタを定義するために行うことができます。
ヨハネスシャウブ-litb 2009年

0
#if defined(__OBJC__)
    #define __bool BOOL
    #include <stdbool.h>
    #define __bool bool
#endif

static inline __bool xor(__bool a, __bool b)
{
    return (!a && b) || (a && !b);
}

定義どおりに機能します。条件は、Objective-Cを使用しているかどうかを検出することです。これは、boolではなくBOOLを要求します(長さが異なります!)


3
これは二重下線ルールに違反しています。
タマシュSzelei

@TamásSzelei前処理されてコンパイラが認識しないため、必ずしもそうではありません。また、Objective-Cの世界では、二重のアンダースコアがかなり一般的です。
Maxthon Chan

プリプロセッサについては良い点ですが、私にとってはこの方法でコードのにおいがします(とにかくtypedefの代わりにマクロを使用するのはなぜですか?)。また、問題はObjective-Cに関するものではありませんでした。
タマシュSzelei

@TamásSzeleiまあ、以前はヘッダーファイルを複数の言語で共有する習慣がありましたが、通常、すべてのヘッダーはObjective-Cからのものです。私の新しいコードはあまり匂いがありませんが、ObjCの習慣を​​守るために、二重の下線がまだ時々使用されています。
Maxthon Chan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.