好吧,你想要激进的语言修改。具体来说,您想创建自己的运算符。准备好了吗?
语法
我将修改语法以使用 C 和 C++ 样式的列表:
if (x in {x0, ...}) ...
此外,我们将让新的 in 运算符应用于定义了begin() 和end() 的任何容器:
if (x in my_vector) ...
有一个警告:它不是一个真正的运算符,因此它必须始终像它自己的表达式一样用括号括起来:
bool ok = (x in my_array);
my_function( (x in some_sequence) );
代码
首先要注意的是,RLM 通常需要一些宏和操作符滥用。幸运的是,对于一个简单的成员谓词来说,滥用实际上并没有那么糟糕。
#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
用法
太棒了!现在我们可以在 all 中使用它,就像您期望 in 运算符有用的方式一样。根据您的特殊兴趣,请参见示例 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";
}
可能的改进
总有一些事情可以针对您的特定目的改进代码。您可以添加一个 ni (not-in)运算符(添加一个新的 thunk 容器类型)。您可以将 thunk 容器包装在命名空间中(一个好主意)。您可以专注于 std::set 之类的东西,以使用 .count() 成员函数而不是 O(n) 搜索。等等。
您的其他顾虑
-
const vs mutable:不是问题;两者都可以与操作员一起使用
-
or 的懒惰:从技术上讲,or 不是懒惰,它是短路的。 std::find() 算法也以同样的方式短路。
- 编译时循环展开:此处并不适用。您的原始代码没有使用循环;虽然
std::find() 确实如此,但可能发生的任何循环展开都取决于编译器。
- 易于扩展到
== 以外的运算符:这实际上是一个单独的问题;您不再关注简单的成员资格谓词,而是正在考虑使用功能性折叠过滤器。完全有可能创建一个算法来做到这一点,但标准库提供了 any_of() 函数,它正是这样做的。 (它只是不如我们的 RLM 'in' 运算符漂亮。也就是说,任何 C++ 程序员都会很容易理解它。这里已经提供了这样的答案。)
希望这会有所帮助。