修改not_null以禁止与nullptr比较

时间:2018-06-27 07:38:24

标签: c++ null cpp-core-guidelines

我正在尝试对赋值进行编译时检查,并针对null进行测试。原因是我正在使用“魔术”非空指针来表示某事物的脱离状态,并且很容易忘记它正在使用该魔术指针并错误地分配和测试nullptr。 (特别是因为这是该代码在历史上一直使用的。)

发现这是人们已经解决的问题,我搜索发现not_nullpart of the C++ Core Guidelines,这听起来很有希望。这是Microsoft的MIT许可实施:

https://github.com/Microsoft/GSL/blob/5cbde3008aa43a9c5f6c219ee15b8388336d4433/include/gsl/pointers#L55

但是它只会停止分配给nullptr,而不是进行比较:

#include <iostream>
#include "include/gsl/gsl"

int main() {
    int i;
    gsl::not_null<int*> ptr (&i);
    /* gsl::not_null<int*> ptr_error (nullptr); */ // this errors

    if (ptr != nullptr)
       std::cout << "No compile error on compare--this prints\n";
}

那是...不幸的。 :-/

  1. 难道不是指导程序员帮助编码程序语义的核心准则吗?

  2. 难道不是有人测试了针对null的指针并根据该指针做出决定是否表明他们不知道自己在做什么,并且源代码很可能会出错吗?

  3. not_null进行较小的修改是什么,将其更改为不允许进行这些比较,例如nullptr == ptrptr == nullptr等?蛮力我只是{= {1}}对==和!=进行一些重载,直到在我想要的情况下给出错误为止,但是我希望使用预先编写的代码是有人会考虑并做得更多通常是“正确”。

2 个答案:

答案 0 :(得分:1)

之所以这样做,可能是因为有时您可能会获得一个指针/智能指针,并希望将其与gsl::not_null进行比较,并且/或者将gsl::not_null传递给模板函数(绝对不知道gsl::not_null提供了什么):

template<class Lhs, class Rhs>
bool hasSameValue(Lhs lhs, Rhs rhs){
  if(lhs == nullptr || rhs == nullptr)
    return lhs == rhs;

  return *lhs == *rhs;
}

gsl::not_null<int*> ptr = /* [...] */;
shared_ptr<int> sptr = /* [...] */;
hasSameValue(ptr, sptr);

如果您仍然想禁止使用nullptr进行检查:

bool operator==(std::nullptr_t nptr) = delete;
bool operator!=(std::nullptr_t nptr) = delete;

将它们标记为已删除就足够了。请注意,gsl::not_null不得继承自定义它们的类。如果它继承自定义它们的类,则只需引发一个异常(即使它只是运行时错误)。

答案 1 :(得分:0)

您只需要为std::nullptr_t类型实现相等运算符:

// Example program
#include <iostream>
#include <string>

class Test
{
public:
  Test(int value) : value(value) {}

  bool operator == (std::nullptr_t n)
  {
      return value == 0;
  }

  bool operator != (std::nullptr_t n)
  {
      return value != 0;
  }

private:
  int value;    
};

int main()
{
    Test a(0);
    Test b(1);
    std::cout << (a == nullptr) << ", " << (b == nullptr) << "\n";
    std::cout << (a != nullptr) << ", " << (b != nullptr) << "\n";
}

或者如果您想要编译错误,只需删除相同的运算符即可。