8

检查是否设置了值的函数的最新技术是什么?

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

最方便的方法是什么?

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*似乎是合乎逻辑的方式,但在 C++11 流中已弃用
  4. explicit operator尚不支持(我的代码必须与旧编译器兼容)

我在读:

4

4 回答 4

6

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

当您使用 C++11 时,explicit operator bool这是要走的路,因为它主要是为这些情况而发明的。

于 2012-11-02T10:53:48.160 回答
2

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

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
}
...

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

于 2013-11-20T15:49:49.050 回答
1

大多数时候,不应使用隐式转换,即使用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,而且他们无法获得一个,因为它是私有的。

于 2012-11-02T11:10:43.013 回答
0

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


检查价值存在

1.正如@j_random_hackerbool isSet()所指出的

最合乎逻辑的方式。源代码(库和应用程序)都是初学者可以理解的。而这符合KISS原则。此外,这可以移植到其他编程语言,如 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

请参阅写得很好的路易斯的回答

5.用operator写优雅if(it)

使用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 年,当前的源代码必须与编译器兼容,而不支持显式转换运算符。在这些编译器上,年复一年地实现了不同的可能性。我将在下一章介绍所有这些内容。


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

本章的源代码灵感来自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) 中使用的解决方案,作为for explicit operator bool()unique_lock和的解决方法。shared_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. @CashCowbool提出的安全成语

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

这要复杂得多,请参阅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
    • 选修的
    • 功能
    • 范围
    • 逻辑(三元组)

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

于 2012-11-05T01:56:41.510 回答