Подтвердить что ты не робот

Gsl:: not_null <T *> vs. std:: reference_wrapper <T> против T &

Основные рекомендации по С++ были представлены недавно (поздравления!), и меня беспокоит тип gsl::not_null. Как указано в I.12: Объявите указатель, который не должен быть нулевым как not_null:

Чтобы избежать разыменования ошибок nullptr. Чтобы повысить производительность избегая избыточных проверок для nullptr.

...

Указывая намерение в источник, исполнители и инструменты могут обеспечить лучшую диагностику, такую ​​как нахождение некоторых классов ошибок посредством статического анализа и выполнение оптимизации, такие как удаление ветвей и нулевые тесты.

Цель ясно. Однако у нас уже есть языковая функция. Указатели, которые не могут быть пустыми, называются ссылками. И хотя ссылки не могут восстанавливаться после их создания, эта проблема решается std::reference_wrapper.

Основное различие между gsl::not_null и std::reference_wrapper я вижу в том, что последнее можно использовать только вместо указателей, в то время как первое работает ни на что nullptr -assignable (цитата из F.17: Используйте not_null, чтобы указать, что значение "null" не является допустимым значением):

not_null предназначен не только для встроенных указателей. Он работает для array_view, string_view, unique_ptr, shared_ptr и другие похожие на указатели типы.

Я представляю таблицу сравнения функций следующим образом:

T&:

  • Не удается сохранить nullptr? - Да
  • Rebindable? - Нет
  • Может использоваться вместо чего-то, кроме указателей? - Нет

std::reference_wrapper<T>:

  • Не удается сохранить nullptr? - Да
  • Rebindable? - Да
  • Может использоваться вместо чего-то, кроме указателей? - Нет

gsl::not_null<T*>:

  • Не удается сохранить nullptr? - Да
  • Rebindable? - Да
  • Может использоваться вместо чего-то, кроме указателей? - Да

Теперь вот вопросы, наконец:

  • Правильно ли я понимаю различия между этими понятиями?
  • Означает ли это, что std::reference_wrapper теперь бесполезен?

PS Я создал теги cpp-core-guidelines и guideline-support-library для этого, надеюсь, правильно.

4b9b3361

Ответ 1

Ссылки не являются указателями, которые не могут быть пустыми. Ссылки семантически сильно отличаются от указателей.

Ссылки имеют назначение значений и семантику сравнения; то есть операции присваивания или сравнения, включающие ссылки, считывают и записывают ссылочное значение. Указатели имеют (контринтуитивно) ссылочное задание и семантику сравнения; то есть операции присваивания или сравнения, включающие указатели, считывают и записывают саму ссылку (т.е. адрес ссылочного объекта).

Как вы заметили, ссылки не могут быть отскок (из-за семантики присваивания значения), но шаблон класса reference_wrapper<T> может быть отскок, потому что он имеет семантику назначения ссылок. Это связано с тем, что reference_wrapper<T> предназначен для использования с контейнерами и алгоритмами STL и не будет корректно вести себя, если его оператор назначения копирования не сделал то же самое, что и его конструктор копирования. Тем не менее, reference_wrapper<T> по-прежнему имеет семантику сравнения значений, как ссылку, поэтому при использовании с контейнерами и алгоритмами STL ведет себя по-разному. Например, set<T*> может содержать указатели на разные объекты с одинаковым значением, а set<reference_wrapper<T>> может содержать ссылку только на один объект с заданным значением.

Шаблон класса not_null<T*> имеет ссылочное назначение и семантику сравнения, например указатель; это указательный тип. Это означает, что он работает как указатель при использовании с контейнерами STL и алгоритмами. Он просто не может быть нулевым.

Итак, вы правы в своей оценке, за исключением того, что вы забыли о семантике сравнения. И нет, reference_wrapper<T> не будет устаревать любым типом типа указателя, потому что он имеет ссылочную семантику сравнения значений.

Ответ 2

Я думаю, что есть еще варианты использования std::reference_wrapper, которые не покрываются gsl::not_null. В принципе, std::reference_wrapper отражает ссылку и имеет преобразование operator T&, а not_null имеет интерфейс указателя с operator->. Один случай использования, который приходит мне на ум сразу, - это создание потока:

void funcWithReference(int& x) { x = 42; }
int i=0;
auto t = std::thread( funcWithReference, std::ref(i) );

Если у меня нет контроля над funcWithReference, я не могу использовать not_null.

То же самое относится к функторам для алгоритмов, и мне пришлось использовать его для привязки boost::signals тоже.

Ответ 3

Вот полезная статья, в которой обсуждаются gsl:not_null: https://visualstudiomagazine.com/articles/2016/06/01/using-the-not_null-template.aspx

Соответствующая часть:

Многие разработчики стараются использовать ссылки как можно больше, потому что ссылки не могут быть нулевыми, но вы не можете повторно использовать ссылку и получить ее ссылаться на что-то другое, как вы можете с помощью указателя. Так что указатели времени - это то, что вы должны использовать, что возвращает вас ко всем что проверка нулевой точки и повторяющийся страх перед исключением с нулевым указателем.