【问题标题】:isSet() or operator void*() or explicit opertor bool() or something else?isSet() 或 operator void*() 或显式 operator bool() 或其他什么?
【发布时间】:2012-11-02 10:50:38
【问题描述】:

检查值是否设置的函数最先进是什么?

例如,下面的迭代器解析单元格。 一些单元格包含一个值,其他单元格是空的。

最方便的方法是什么?

struct iterator 
{                                  //usage:
  bool isset() const               // if (it.isset()) 
  bool isSet() const               // if (it.isSet()) 
  bool empty() const               // if (it.empty()) 

  bool is_set()   const            // if (it.is_set()) 
  bool is_valid() const            // if (it.is_valid()) 

  operator void*() const;          // if (it) 

  explicit operator bool() const;  // if ((bool)it) or if(it) //thanks @stijn
  operator          bool() const;  // if (it) //why not implicit conversion?

  bool operator!() const;          // if (!!it)

  //throwing exception as pointed out by @MatthieuM
  Type get() { if (isSet()) return value_; else throw; }
  //usage:
  //     try {                    // if (it.isSet()) {
  //        Type x = it.get();    //    Type x = it.get();
  //     }                        // }
  //     catch (...) {            // else {
  //        //empty               //    //empty
  //     }                        // }

  //please feel free to propose something different
  ...
};

反思:

  1. 我的老板不明白isset() => 改名为isSet()
  2. empty() 更多的是关于容器收集,而不仅仅是一个单元格:(
  3. operator void* 似乎是合乎逻辑的方式,但 deprecated in C++11 streams
  4. explicit operatornot yet supported(我的代码必须与旧编译器兼容)

我正在阅读:

【问题讨论】:

  • 请使用isSet()isset(),随心所欲。它是 (a) 显式的,(b) 适用于每个编译器,(c) 让您有空间在以后指定其他谓词 (isValid()?isNice()?),最重要的是 (d) 消除了未来代码维护者(包括你自己)会浪费时间试图理解它。附言如果你想走explicit operator bool 路线,那么你“应该”写if (static_cast<bool>(it))。现在看起来不那么性感了是吗? :-P
  • @j_random_hacker "if (static_cast<bool>(it))" - 为什么?在 if 条件中使用它已经执行了显式转换。或者你有更概念性的理由不喜欢if(it)
  • @ChristianRau:你说得对——看来ifwhile 提供了显式转换的效果,所以可以使用if (it),这很好。不过,我认为我给出的其他理由是成立的。
  • “我认为我给出的其他理由仍然存在” - 当然。
  • 注意:在任何情况下,assertthrow 以防用户尝试访问迭代器内的对象(即使未设置)总是一个很好的问题。

标签: c++ c++11 operator-keyword c++03 explicit-conversion


【解决方案1】:

void* 有问题,因为它是一个有效的转换序列,但在某些情况下并非如此。许多人在 C++03 中使用有时称为“safe bool idiom”,其中您有一个包含私有类型的本地成员函数指针类型,因此没有人可以在您的类之外拥有它的实例。但是,您可以返回它并至少检查真/假。

当您使用 C++11 时,explicit operator bool 是最佳选择,因为它主要是针对这些情况而发明的。

【讨论】:

  • 所以explicit operator bool() const 你可以只写if( it ) 而不是if( (bool) it ),对吧?
  • 我最喜欢使用 op void* 的技巧是 +count。不幸的是,它在 c++11 中不起作用:-)
  • @stijn:确实,我们的目标是让if(it) 做你想做的事,但仅此而已。显式转换也可以,但不是必需的。
  • @stijn - 对。该对象“根据上下文转换为bool”。
【解决方案2】:

我印象深刻的是,来自 Imperfect C++: Practical Solutions [...]explicit_cast<T> 没有被提及。这个概念非常简单——你设置一个伪关键字,它实际上是一个模板类,实现了你想要的转换。我一直在我的own C++ backports library 中使用它,没有任何重要问题。

class MyClass {
  ...implementation
  operator explicit_cast<bool> () const { 
      (return a bool somehow)
  }
};

您使用伪关键字就像您期望的那样:

MyClass value;
...
if ( explicit_cast<bool>(myobject) )  {
  do something
} else {
  do something else
}
...

最棒的是,这个解决方案可以完美地映射到 C++11 中的原生 explicit operator,从而基本上实现零开销和原生语法。因此,它也比试图弄清楚是否调用“isset”、“is_set”、“is_Set”、“isSet”等更通用......

【讨论】:

  • 感谢@Luis 的贡献。由于我的投票没有给您任何意见(社区 wiki),因此我对您提出的另一个答案和问题进行了投票。你可能同意写if ( explicit_cast&lt;bool&gt;(myobject) )不是很优雅,不是吗?干杯;)
  • 谢谢。当然,这不是很优雅-对于特定于布尔的情况,我宁愿写一个特定的bool_cast,但我也反对伪关键字扩散^.^-无论如何,我觉得并希望不优雅被原生方式所抵消它移植到 C++11。
【解决方案3】:

大多数时候,不应使用隐式转换,即在代码中使用 operator bool() 之类的表达式。

当您希望能够在 if 语句中使用您的类的实例时,您通常会创建一个隐式转换,但要创建一个成员函数原型的签名,您将指向一个无操作私有函数或为 NULL 取决于状态。

您还会经常为您的班级重载bool operator!() const。由于这将使用与隐式转换相同的逻辑,因此您通常会根据另一个来实现一个。

类似:

private:
    struct MyPrivateType {};
    void MyPrivateFunc( MyPrivateType ) {}
public:
    typedef void (&iterator::*)( MyPrivateType ) bool_func;

    operator bool_func() const
    {
        return operator!() ? static_cast<bool_func>(0) : MyPrivateFunc;
    }

没有人可以调用您从指针返回的函数,因为它需要一个 MyPrivateType,而他们无法获得一个,因为它是私有的。

【讨论】:

  • 注意:typedef 也可以是private
【解决方案4】:

感谢您的所有 cmets/answers/contributions。在这里,我将网上找到的不同想法和其他想法融合在一起(我阅读了很多文档和源代码)。


检查值是否存在

1。 bool isSet()@j_random_hacker 指出

最合乎逻辑的方式。源代码(库和应用程序)都是初学者可以理解的。这适合KISS principle。此外,这可以移植到其他编程语言,如 Java...

Library:               |    Application:
                       |
struct iterator        |
{                      |
  bool isSet() const   |   if (it.isSet())
  {                    |   {
    return p;          |       int v = it.get();
  }                    |       //get() may also call isSet()
                       |       
  int get() const      |       //continue processing
  {                    |   }
     return *p;        |   else //value is not set
  }                    |   {
                       |       //do something else
  int* p;              |   }
};                     |

如果函数get() 不检查isSet() 并且(应用程序的)开发人员忘记调用isSet()(在get() 之前),则应用程序代码可能会崩溃(分段错误)。

另一方面,如果get() 函数调用isSet(),则isSet() 处理会执行两次。然而,最近的编译器应该避免这种第二次不必要的isSet() 处理。

2。返回我的一位同事建议的标志值或默认值

Library:               |    Application:
                       |
struct iterator        |   int i = it.get()
{                      |   if (i >= 0)
  int get() const      |   {   
  {                    |     unsigned short v = i;
    if(p) return *p;   |
    else  return -1;   |     //continue processing
  }                    |   }   
                       |   else //value is not set
  unsigned short* p;   |   {
};                     |     //do something else
                       |   }

3。 @Matthieu M 指出的抛出异常

有些人认为异常不利于二进制代码优化。但是,如果 throw exception 是内联的,最好的编译器可能会优化二进制代码,并且比
此外,此解决方案可能允许最佳优化的二进制代码,因为isSet() 被调用了两次。但这取决于编译器的优化能力。

图书馆:

struct iterator
{
  bool get() const  
  { 
     if (isSet()) return *p; 
     else throw; 
  }

private:
  bool isSet() const  { return ....; }      

  ....
};

应用:

int value;
try
{
   value = it.get();
}
catch (...)
{
   value = 0; // default value
}

4。使用operator explicit_cast&lt;bool&gt; () const

请参考写得好的Luis's answer

5。用operator写优雅的if(it)

使用explicit conversion operators introduced in C++11可以很好地实现这个解决方案。

图书馆:

struct iterator
{
  explicit operator bool() const  { return ....; }

  ....
};

应用:

int value;
if (it)      //very elegant C++ fashion
{
   value = it.get();
}
else
{
   value = 0; // default value
}

但是,我们仍处于 2012 年,当前的源代码必须与不支持显式转换运算符的编译器兼容。在这些编译器上,年复一年地实现了不同的可能性。我将在下一章介绍所有这些内容。


在 C++11 之前启用语句if(it)

本章的源代码灵感来自Bjarne Stroustrup在2004年写的书More C++ idioms,更具体地说是@PlasmaHH指出的The Safe Bool Idiom部分。

1。隐式operator bool

explicit不可用时,我们可以只使用隐式转换运算符

图书馆:

struct iterator
{
  operator bool() const  { return ....; } //implicit conversion

  ....
};

应用:

int value;
if (it)      //this works very well!
{
   value = it.get();
}
else
{
   value = 0; // default value
}

// But these other instructions are also correct :(
int integer = it;   //convert it to bool, then convert bool to int
if (-6.7 < it)      //.................., then convert bool to double, and compare
  it << 1;

2。 operator!

这是boost::thread (v1.51) 中使用的解决方案,作为explicit operator bool() 的解决方法,用于unique_lockshared_lockupgrade_lockupgrade_to_unique_lock

图书馆:

struct iterator
{
  bool operator!() const  { return ....; }

  ....
};

应用:

int value;

if (!!it)      // !! looks strange for many developers
{
   value = it.get();
}
else
{
   value = 0; // default value
}

if (it)    //ERROR: could not convert ‘it’ from ‘iterator’ to ‘bool’
{
   value = it.get();
}

3。 operator void*

这是 STL 流使用的解决方案。例如参考文件bits/basic_ios.h (std::basic_ios)。

图书馆:

struct iterator
{
  operator void*() const  { return ....; }

  ....
};

应用:

int value;
if (it)      //this works very well!
{
   value = it.get();
}
else
{
   value = 0; // default value
}

// But these other instructions are also correct :(
delete it;  //just a warning: deleting 'void*' is undefined
if (it > std::cin) //both are converted to void*
  void* r = it;

4。隐式转换为未定义的嵌套 class

这个解决方案是Don Box在1996年提出的。

图书馆:

struct iterator
{
private:
  class nested; //just a forward declaration (no definition)
  int* v_;
public:
  operator nested*() const  { return v_ ? (nested*)this : 0; }
};

应用:

int value;
if (it)      //this works very well!
{
   value = it.get();
}
else
{
   value = 0; // default value
}

// But these other instructions are also correct :(
iterator it2; 
if (it < it2) 
  int i = (it == it2);

5。 @CashCow 提出的安全bool 成语

Bjarne Stroustrup 提出了utimate solution 没有缺点。以下是简化版。

图书馆:

struct iterator
{
private:
  typedef bool (iterator::*bool_type)() const;
  bool private_() const {}
  int* v_;

public:
  operator bool_type() const  { return v_ ? &iterator::private_ : 0; }
};

//forbids it1 == it2
template <typename T>
bool operator == (const iterator& it,const T& t) { return it.private_(); }

//forbids it1 != it2
template <typename T> 
bool operator != (const iterator& it,const T& t) { return ! (it == t); } 

应用:

int value;
if (it)      //this works very well!
{
   value = it.get();
}
else
{
   value = 0; // default value
}

// All other instructions fail to compile
iterator it2;
if (it >  it2)  ;  //ERROR:  no match for ‘operator>’ in ‘it > it2’
if (it == it2)  ;  //ERROR: ‘bool iterator::private_() const’ is private
if (it != it2)  ;  //same error

6。可重复使用的保险箱bool 成语

这个比较复杂,源码请参考Wikibooks

图书馆:

struct iterator : safe_bool <iterator> //I do not want virtual functions
{
   bool boolean_test() const  { return ....; }

   ....
};

最近的 STL 和 boost 提供设施。一些例子:

  • GNU STL --> 查看文件 tr1/functional 和 exception_ptr.h
  • 每个 Boost 组件都使用自己的safe_bool
    • Spirit --> 查看文件 spirit/include/classic_safe_bool.hpp 和 spirit/home/classic/core/safe_bool.hpp
    • IOstream --> 查看文件 iostreams/device/mapped_file.hpp
    • 参数 --> 参数/aux_/maybe.hpp
    • 可选
    • 功能
    • 范围
    • 逻辑(tribool)

但是 Matthew Wilson 在他的书 Imperfect C++ 中说,safe_bool 可能会导致对未实现 Empty Base Optimization 的编译器的大小惩罚。尽管大多数现代编译器在单继承方面都会这样做,但多继承可能会造成大小损失。

【讨论】:

    猜你喜欢
    • 2011-08-30
    • 1970-01-01
    • 2021-08-14
    • 2021-04-16
    • 1970-01-01
    • 1970-01-01
    • 2020-04-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多