Let's simplify the example. Your code does not work for the same reason that this one will not:
int x = 2;
if(x == 1 || 2) { ... }
That's because ||
operator does not concatenate in such way. The above example evaluates the x == 1
(false
) and that ORs it (||
) with 2
, like so:
if(false || 2) { ... } // after evaluating of x == 1
We now have false || 2
. In C++
, any non-zero numeric value evaluates to true
, so we end up with false || true
, which is true
.
But it seems like it works in this example, right?
It only appears to. Replace the 2
with 3
and the code will still be evaluated to true
, even though x
is neither 1
nor 3
.
How to fix the issue?
In case of small combinations, one should just correctly provide all the expressions:
if(x == 1 || x == 2) { ... }
instead of:
if(x == 1 || 2) { ... }
So in your example you would need:
if(classSelection != "C" || classSelection != "c" || classSelection != "F" || ... )
But that still wouldn't do what you want. You don't want to know whether classSelection
is not equal to, for instance, "C"
or not equal to "c"
. You want to know if it's not equal to "C"
and not equal to "c"
, so you'd actually want:
if(classSelection != "C" && classSelection != "c" && classSelection != "F" && ... )
And your example would require some typing. Another approach, slightly less eficient, but arguably more readable, would be to store all the possible matches in a data structure and use a standard algorithm, such as std::all_of
:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
int main() {
std::vector<const char*> not_accepted= {
"C", "c", "F", "f", "P", "p", "R", "r", "W", "w"
};
std::string choice = "f"; // provided by user
bool valid = std::all_of(not_accepted.cbegin(), not_accepted.cend(),
[&](auto arg){
return choice != arg;
});
std::cout << "valid? - " << std::boolalpha << valid;
}
std::all_of
takes a range and a predicate and returns true
, if all the elements satisfy the predicate. Here, the range is our not_accepted
vector and our predicate is represented as a lambda, which will compare every single element of that vector with our choice
string.
To optimise, one could replace std::vector
with a std::set
or std::unordered_set
and check for elements being present in a set. That would get rid of an algorithm call:
#include <iostream>
#include <unordered_set>
#include <string>
int main() {
std::unordered_set<const char*> not_accepted = {
"C", "c", "F", "f", "P", "p", "R", "r", "W", "w"
};
std::string choice = "f";
bool valid = not_accepted.find("f") == not_accepted.end();
std::cout << "valid? - " << std::boolalpha << valid;
}