GSL :: NOT_NULL & л; Т * & GT; против std :: reference_wrapper & lt; T & gt; против T & amp;

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

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

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

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

Основное различие между gsl::not_null а также std::reference_wrapper Я вижу в том, что последний может использоваться только вместо указателей, в то время как первый работает на что угодно nullptrприсваивается (цитата из 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? — да
  • Может быть использован вместо чего-то, кроме указателей? — да

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

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

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

34

Решение

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

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

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

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

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

19

Другие решения

Я думаю, что есть еще варианты использования для 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 тоже.

10

По вопросам рекламы [email protected]