in运算符在C ++中的实现

你好 今天,我希望向您展示一些魔术。 我的爱好是用C ++发明各种看似不可能的部分,这有助于我学习各种语言的精妙之处,或者只是为了获得乐趣。 in运算符有几种语言,例如Python,JS。 但是他们没有将其引入C ++,但有时我希望它成为现实,所以为什么不实现它。

std::unordered_map<std::string, std::string> some_map = { { "black", "white" }, { "cat", "dog" }, { "day", "night" } }; if (auto res = "cat" in some_map) { res->second = "fish"; } 


我认为操作员应该如何工作是显而易见的。 他带走了左边的对象,并检查在右边指示的对象中是否存在该对象,该对象不一定是集合。 本身没有通用解决方案,就像其他操作员也没有通用解决方案一样,因此,发明了使它们过载的可能性。 因此,对于in运算符,您需要实现类似的机制。

过载看起来像这样。

 bool operator_in(const string& key, const unordered_map<string, string>& data) { return data.find(key) != data.end(); } 

我认为主意很明确,就是物种的表达。

  "some string" in some_map 

它应该变成一个函数调用。

  operator_in("some string", some_map) 

使用现有的操作员重载功能,实现此机制非常简单。 in运算符本身本质上是一个执行乘法的宏。

  #define in *OP_IN_HELP{}* 

在这种情况下, OP_IN_HELP是一个空类,仅用于选择正确的重载。

  class OP_IN_HELP {}; template<class TIn> OP_IN_LVAL<TIn> operator*(const TIn& data, const OP_IN_HELP&) { return OP_IN_LVAL<TIn>(data); } 

运算符是样板,它允许您接受任何类型作为第一个参数。 现在,我们需要以某种方式获取正确的对象,而又不丢失左侧的对象。 为此,我们实现了OP_IN_LVAL类, 该类将存储我们的左对象。

  template<class TIn> struct OP_IN_LVAL { const TIn& m_in; OP_IN_LVAL(const TIn& val) : m_in(val) {}; }; 

由于对象本身在表达式运行时会保持活动状态,因此如果我们对这个对象保持不变的引用,就无需担心。 现在剩下要做的就是实现内部乘法运算符,它将把重载运算符的结果返回给我们,它本身就是模板。

  template<class TIn> struct OP_IN_LVAL { const TIn& m_in; OP_IN_LVAL(const TIn& val) : m_in(val) {}; template<class TWhat> bool operator*(const TWhat& what) const { return operator_in(m_in, what); } }; 

实际上,该解决方案已经可以使用,但是它是有限的,不允许我们以这种方式编写。

  if (auto res = "true" in some_map) { res->second = "false"; } 

为了使我们有这样的机会,我们需要抛出重载运算符的返回值。 如何执行此操作有两个版本,一个使用C ++ 14的功能,另一个使用C ++ 11的一部分。

  template<class TIn> struct OP_IN_LVAL { const TIn& m_in; OP_IN_LVAL(const TIn& val) :m_in(val) {}; //   C++14 template<class TWhat> auto operator*(const TWhat& what) const { return operator_in(m_in, what); } //   C++11 template<class TWhat> auto operator*(const TWhat& what) const -> decltype(operator_in(m_in, what)) { return operator_in(m_in, what); } //       //       template<class TWhat> auto operator*(TWhat& what) const -> decltype(operator_in(m_in, what)) { return operator_in(m_in, what); } }; 

由于我主要在Visual Studio 2013中工作,所以我仅限于C ++ 11的框架,而C ++ 11中的解决方案将在C ++ 14中成功运行,因此建议您选择它。

unordered_map的通用in运算符的示例实现。

 template<class TIterator> class OpInResult { bool m_result; TIterator m_iter; public: OpInResult(bool result, TIterator& iter) : m_result(result), m_iter(iter) {} operator bool() { return m_result; } TIterator& operator->() { return m_iter; } TIterator& data() { return m_iter; } }; template<class TKey, class TVal> auto operator_in(const TKey& key, std::unordered_map<TKey, TVal>& data) -> OpInResult<typename std::unordered_map<TKey, TVal>::iterator> { auto iter = data.find(key); return OpInResult<typename std::unordered_map<TKey, TVal>::iterator>(iter != data.end(), iter); } template<class TKey, class TVal> auto operator_in(const char* key, std::unordered_map<TKey, TVal>& data) -> OpInResult<typename std::unordered_map<TKey, TVal>::iterator> { auto iter = data.find(key); return OpInResult<typename std::unordered_map<TKey, TVal>::iterator>(iter != data.end(), iter); } 

OpInResult类使可以覆盖强制转换运算符,这使我们可以在if中使用它。 它还覆盖了arrow运算符,该运算符使您可以将自己屏蔽为返回unordered_map.find()的迭代器。

可以在此处找到示例cpp.sh/7rfdw

我还想谈一谈这种解决方案的某些功能。
Visual Studio在使用位置实例化模板,这意味着重载函数本身必须在使用运算符之前声明,但可以在OP_IN_LVAL声明之后声明 。 GCC反过来在声明位置(当它自身被使用时)实例化模板,这意味着必须在声明OP_IN_LVAL类之前声明重载的语句。 如果还不清楚这是什么意思,那么这里有个例子。 cpp.sh/5jxcq在这段代码中,我刚刚遭受过载操作如下OP_IN_LVAL类的声明,他停止了GCC进行编译(除非你用-fpermissive标志编译),但在Visual Studio编译成功。

在C ++ 17中,可以像这样编写。

  if (auto res = some_map.find("true"); res != some_map.end()) { res->second = "false"; } 

但是在我看来,视图的设计

  if (auto res = "true" in some_map) { res->second = "false"; } 

看起来更好。

有关重载的更多示例,请参见github.com/ChaosOptima/operator_in

基于该运算符的实现原理,实现也将没有问题
以及其他运算符和表达式。

  negative = FROM some_vector WHERE [](int x){return x < 0;}; 

聚苯乙烯
我想知道您是否对此类主题感兴趣,在这里有什么要写的吗? 并且您想知道如何实现其他有趣的事情吗?

空条件运算符

  auto result = $if some_ptr $->func1()$->func2()$->func3(10, 11, 0)$endif; 

模式匹配

  succes = patern_match val with_type(int x) { cout << "some int " << x << '\n'; } with_cast(const std::vector<int>& items) { for (auto&& val : items) std::cout << val << ' '; std::cout << '\n'; } with(std::string()) [&](const std::string&) { cout << "empty string\n"; } with(oneof<std::string>("one", "two", "three")) [&](const std::string& value) { cout << value << "\n"; } with_cast(const std::string& str) { cout << "some str " << str << '\n'; } at_default { cout << "no match"; }; 

字符串枚举

  StringEnum Power $def ( POW0, POW1, POW2 = POW1 * 2, POW3, POW4 = POW3 + 1, POW8 = POW4 * 2, POW9, POW10 ); to_string(Power::POW0) from_string<Power>("POW0") 

Source: https://habr.com/ru/post/zh-CN419579/


All Articles