-2

I seem to be having a problem with an if statement in one of the functions of my program.

I want the line of code to stop until the correct variable is displayed, while also outputting an error message. However, the code continues no matter if the wrong or correct variable is entered, and the error message is displayed with it as well.

Here is the section of code with the if statement

string get_class(){

    string char_class;

    cout<<"Choose A Character Class!"<<endl;    
    cout<<"The Cleric, a holy disciple!   Select C."<<endl;
    cout<<"The Fighter, an unstoppable warrior!  Select F."<<endl;
    cout<<"The Paladin, a holy warrior!  Select P."<<endl;
    cout<<"The Rogue, a sneaky thief!  Select R."<<endl;
    cout<<"The Wizard, a wizened magician!  Select W."<<endl;
    cin>>classSelection;

    if(classSelection != "C"||"c"||"F"||"f"||"P"||"p"||"R"||"r"||"W"||"w")
        cout<<"Please Enter Given Selections"<<endl;

    return char_class; 

}

I apologize if not enough of the program was given, or if everything in this snippet looks unorganized.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190

2 Answers2

3

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;
}
Fureeish
  • 12,533
  • 4
  • 32
  • 62
3

My personal favourite

while("CcFfPpRrWw"s.find(classSelection) == string::npos)
{
    cout << "Please Enter Given Selections" << endl;
    cin >> classSelection;
}

In your original code you made the rookie mistake of or'ing possible candidates for selection. These will individually evaluate to true, causing the whole expression to always evaluate to true.

My code puts all the choices in a temporary string and searches it for the inputted char. string::npos is returned if it is not found, triggering the while loop.

Michael Surette
  • 701
  • 1
  • 4
  • 12
  • Simpler and better solution that my approaches using `std::vector` or `std::[unordered]_set`. I somehow always keep forgetting about `std::string::find`. If you also provide explanation to why the OP's approach does not work and how yours work, I will happily upvote your answer. – Fureeish Mar 08 '19 at 19:29