Модификация not_null для запрета сравнений с nullptr - PullRequest
0 голосов
/ 27 июня 2018

Я пытаюсь получить проверку во время компиляции для назначения и проверки на ноль. Причина в том, что я использую «магический» ненулевой указатель для представления отключенного состояния чего-либо, и легко забыть, что он использует этот магический указатель, и по ошибке назначить и проверить на nullptr. (Тем более, что это то, что код использовал исторически.)

Понимая, что это была проблема, которую люди уже решили, я искал и нашел not_null как часть C ++ Core Guidelines , что звучало многообещающе. Вот MIT-лицензированная реализация от Microsoft:

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. Какое незначительное изменение в not_null, чтобы изменить его, чтобы запретить эти сравнения, например nullptr == ptr, ptr == nullptr и т. Д.? Грубой силой я бы просто = delete; перегрузил некоторые = = и! =, Пока это не дало бы ошибки в случае, который я хотел, но я надеялся, что с помощью предварительно написанного кода кто-то подумал бы об этом и сделал бы это более широко " право».

Ответы [ 2 ]

0 голосов
/ 27 июня 2018

Одна из причин, по которой вы можете сделать это, вероятно, заключается в том, что иногда вы можете получить указатель / умный указатель и захотеть сравнить его с 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);

Если вы все еще хотите запретить проверки w / nullptr:

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

Достаточно пометить их как удаленные. Обратите внимание, что gsl::not_null не должен наследовать от класса, который их определяет, чтобы сделать это. Если он наследует от класса, который его определяет, просто сгенерируйте исключение (даже если это будет только ошибка времени выполнения).

0 голосов
/ 27 июня 2018

Вам просто нужно реализовать операторы равенства для типа 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";
}

или если вам нужна ошибка компиляции, просто удалите те же операторы.

...