Proper manual logical XOR implementation depends on how closely you want to mimic the general behavior of other logical operators (|| and &&) with your XOR. There are two important things about these operators: A) they guarantee short-circuit evaluation, B) they introduce a sequence point.
XOR evaluation, as you understand, cannot be short-circuited. So A is out of question. But what about B? If you don't care about B, then with normalized (i.e. bool) values operator != does the job of XOR in terms of the result. And the operands can be easily normalized with unary !, if necessary.
If you care about the extra sequence point though, neither != nor bitwise ^ is the proper way to implement XOR. One possible way to do XOR(a, b) correctly might look as follows
a ? !b : b
This is actually as close as you can get to making a homemade XOR "similar" to || and &&. This will only work, of course, if you implement your XOR as a macro. A function won't do, since the sequencing will not apply to function's arguments.
Someone might say though, that the only reason of having a sequence point at each && and || is to support the short-circuited evaluation, and thus XOR does not need one. This makes sense, actually. Yet, it is worth considering having a XOR with a sequence point in the middle.
For example, the following expression
++x > 1 && x < 5
has defined behavior and specificed result in C/C++ (with regard to sequencing at least). So, one might reasonably expect the same from user-defined logical XOR, as in
XOR(++x > 1, x < 5)
while a !=-based XOR doesn't have this property.