Alrighty then, you want Radical Language Modification. Specifically, you want to create your own operator. Ready?
Syntax
I'm going to amend the syntax to use a C and C++-styled list:
if (x in {x0, ...}) ...
Additionally, we'll let our new in operator apply to any container for which begin() and end() are defined:
if (x in my_vector) ...
There is one caveat: it is not a true operator and so it must always be parenthesized as it's own expression:
bool ok = (x in my_array);
my_function( (x in some_sequence) );
The code
The first thing to be aware is that RLM often requires some macro and operator abuse. Fortunately, for a simple membership predicate, the abuse is actually not that bad.
#ifndef DUTHOMHAS_IN_OPERATOR_HPP
#define DUTHOMHAS_IN_OPERATOR_HPP
#include <algorithm>
#include <initializer_list>
#include <iterator>
#include <type_traits>
#include <vector>
//----------------------------------------------------------------------------
// The 'in' operator is magically defined to operate on any container you give it
#define in , in_container() =
//----------------------------------------------------------------------------
// The reverse-argument membership predicate is defined as the lowest-precedence 
// operator available. And conveniently, it will not likely collide with anything.
template <typename T, typename Container>
typename std::enable_if <!std::is_same <Container, T> ::value, bool> ::type
operator , ( const T& x, const Container& xs )
{
  using std::begin;
  using std::end;
  return std::find( begin(xs), end(xs), x ) != end(xs);
}
template <typename T, typename Container>
typename std::enable_if <std::is_same <Container, T> ::value, bool> ::type
operator , ( const T& x, const Container& y )
{
  return x == y;
}
//----------------------------------------------------------------------------
// This thunk is used to accept any type of container without need for 
// special syntax when used.
struct in_container
{
  template <typename Container>
  const Container& operator = ( const Container& container )
  {
    return container;
  }
  template <typename T>
  std::vector <T> operator = ( std::initializer_list <T> xs )
  {
    return std::vector <T> ( xs );
  }
};
#endif
Usage
Great! Now we can use it in all the ways you would expect an in operator to be useful. According to your particular interest, see example 3:
#include <iostream>
#include <set>
#include <string>
using namespace std;
void f( const string& s, const vector <string> & ss ) { cout << "nope\n\n"; }
void f( bool b ) { cout << "fooey!\n\n"; }
int main()
{
  cout << 
    "I understand three primes by digit or by name.\n"
    "Type \"q\" to \"quit\".\n\n";
  while (true)
  {
    string s;
    cout << "s? ";
    getline( cin, s );
    // Example 1: arrays 
    const char* quits[] = { "quit", "q" };
    if (s in quits) 
      break;
    // Example 2: vectors
    vector <string> digits { "2", "3", "5" };
    if (s in digits)
    {
      cout << "a prime digit\n\n";
      continue;
    }
    // Example 3: literals
    if (s in {"two", "three", "five"})
    {
      cout << "a prime name!\n\n";
      continue;
    }
    // Example 4: sets
    set <const char*> favorites{ "7", "seven" };
    if (s in favorites)
    {
      cout << "a favorite prime!\n\n";
      continue;
    }
    // Example 5: sets, part deux
    if (s in set <string> { "TWO", "THREE", "FIVE", "SEVEN" })
    {
      cout << "(ouch! don't shout!)\n\n";
      continue;
    }
    // Example 6: operator weirdness
    if (s[0] in string("014") + "689")
    {
      cout << "not prime\n\n";
      continue;
    }
    // Example 7: argument lists unaffected    
    f( s, digits );
  }
  cout << "bye\n";
}
Potential improvements
There are always things that can be done to improve the code for your specific purposes. You can add a ni (not-in) operator (Add a new thunk container type). You can wrap the thunk containers in a namespace (a good idea). You can specialize on things like std::set to use the .count() member function instead of the O(n) search. Etc.
Your other concerns
- constvs- mutable: not an issue; both are usable with the operator
- laziness of or: Technically,oris not lazy, it is short-circuited. Thestd::find()algorithm also short-circuits in the same way.
- compile time loop unrolling : not really applicable here. Your original code did not use loops; while std::find()does, any loop unrolling that may occur is up to the compiler.
- easy to extend to operators other than ==: That actually is a separate issue; you are no longer looking at a simple membership predicate, but are now considering a functional fold-filter. It is entirely possible to create an algorithm that does that, but the Standard Library provides theany_of()function, which does exactly that. (It's just not as pretty as our RLM 'in' operator. That said, any C++ programmer will understand it easily. Such answers have already been proffered here.)
Hope this helps.