Изменение not_null для запрета сравнений с nullptr
Я пытаюсь получить проверку во время компиляции для назначения и проверки на ноль. Причина в том, что я использую "магический" ненулевой указатель для представления отключенного состояния чего-либо, и легко забыть, что он использует этот магический указатель, и по ошибке назначить и проверить nullptr. (Тем более, что это было то, что код использовал исторически.)
Понимая, что это была проблема, которую люди уже решили, я искал и нашел not_null
как часть C++ Core Guidelines, что звучало многообещающе. Вот MIT-лицензированная реализация от Microsoft:
https://github.com/Microsoft/GSL/blob/5cbde3008aa43a9c5f6c219ee15b8388336d4433/include/gsl/pointers
Но это только останавливает присваивания 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";
}
Это прискорбно.:-/
Не является ли смысл основных рекомендаций помочь программистам кодировать семантику программы? Разве кто-то не проверяет указатель на нуль и не принимает решения, основанные на том, что он не знает, что делает, а исходный код может быть ошибочным?
Какой хороший минор редактировать
not_null
изменить его, чтобы запретить эти сравнения, напримерnullptr == ptr
,ptr == nullptr
, так далее? Грубая сила, я бы просто= delete;
некоторые перегрузки == и!= до тех пор, пока это не привело к ошибкам в нужном мне случае, но я надеялся на использование заранее написанного кода, что кто-то подумал бы об этом и сделал бы это в целом "правильно".
2 ответа
Одна из причин, по которой вы можете сделать это, вероятно, заключается в том, что иногда вы можете получить указатель / умный указатель и захотите сравнить его с 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
не должен наследовать от класса, который определяет их, чтобы сделать это. Если он наследуется от класса, который его определяет, просто сгенерируйте исключение (даже если это будет только ошибка времени выполнения).
Вам просто нужно реализовать операторы равенства для 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";
}
или если вы хотите ошибку компиляции, просто удалите те же операторы.