isSet()或运算符void *()或显式运算符bool()或其他?

时间:2012-11-02 10:50:38

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

关于检查值是否设置的功能的最新技术是什么?

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

最方便的方法是什么?

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(我的代码必须符合旧编译器)
  5. 我正在读:

4 个答案:

答案 0 :(得分:6)

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

当您使用C ++ 11时,explicit operator bool是可行的方法,因为它主要是针对这些情况而发明的。

答案 1 :(得分: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”等更通用...

答案 2 :(得分:1)

大多数情况下,不应使用隐式转换,即在代码中使用operator bool()等表达式。

如果您希望能够在if语句中使用您的类的实例,您通常会创建一个隐式转换,但是对于成员函数原型的签名,您将指向一个 - op private function或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,而且它们不能得到一个,因为它是私有的。

答案 3 :(得分:0)

感谢您的所有意见/答案/贡献。在这里,我合并了网上发现的不同想法和其他想法(我已阅读了大量文档和源代码)。


检查值存在

1。 j_random_hacker

指出的bool isSet()

最合乎逻辑的方式。源代码(库和应用程序)都是初学者可以理解的。这符合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<bool> () 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)

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

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)中使用的解决方案,作为unique_lockshared_lockupgrade_lock和{{3} explicit operator bool()的变通方法}}

库:

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流使用的解决方案。例如,请参阅文件upgrade_to_unique_lockstd::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

该解决方案由bits/basic_ios.h于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。由Don Box

提供的安全bool习语

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

库:

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成语

这要复杂得多,请参阅utimate solution了解源代码。

库:

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

   ....
};

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

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

但是马修威尔逊在他的书Wikibooks中说safe_bool可能导致编制者没有实施Imperfect C++的规模惩罚。虽然大多数现代编译器在单继承时都会这样做,但是可能存在多重继承的大小惩罚。