Является ли правило F19 основных рекомендаций Cpp неполным?

Основное руководство Cpp F19 сообщает нам

Отметьте функцию, которая принимает параметр TP&& (где TP — имя параметра типа шаблона) и делает с ним что-либо, кроме std::пересылки ровно один раз по каждому статическому пути.

Буквально это означает что-то вроде

template<typename R, typename V> bool contains(R&& range, const V& value)
{
  return std::find(range, value) != range.end();
}

Должен быть помечен, потому что std::forward не вызывался (и даже если бы он был вызван, он бы вызвал .end()).

Но, насколько я понимаю, эта реализация является правильной и разумной. Мне нужна универсальная ссылка R&&, потому что в реальном мире она нужна мне для привязки к значениям const& (1, см. ниже), но она также нужна мне для привязки к значениям. который не может стать константой (потому что begin() и end() не являются константами) (2). Если бы я сменил подпись на contains(const R& range, const V& value), она была бы несовместима с большинством просмотров.

int main()
{
  const std::vector<int> vs{1, 2, 3, 4};
  std::cout << contains(vs, 7);  // (1)
  std::cout << contains(vs | std::views::transform(squared), 7);  // (2)
}

Вопрос: F19 упускает какой-то крайний случай или я упускаю какую-то деталь? Возможная реализация std::contains также передается R как универсальная ссылка без вызова std::forward.

Альтернатива содержит реализацию с использованием std::forward (но также и .end(), следовательно, она также не совместима с F19):

template<typename R, typename V> bool contains(R&& range, const V& value)
{
  const auto& end = range.end();
  return std::find(std::forward<R>(range), value) != end;
}

Да, это правило звучит как бред. Основные рекомендации не идеальны.

HolyBlackCat 23.07.2024 09:46

Кажется, это правило касается именно следующего случая: если объект должен быть передан в другой код и не использоваться этой функцией напрямую, мы хотим сделать эту функцию независимой от аргументов const-ness и rvalue-ness. Хотя Я не вижу какого-либо правила, описывающего нужный вам случай - функция игнорирует const-ность, но использует значение.

Yksisarvinen 23.07.2024 09:56

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

Ahmed AEK 23.07.2024 10:13

Вы уверены, что ваш код компилируется?

n. m. could be an AI 23.07.2024 11:07

что бы вы добавили в руководство, чтобы оно не соответствовало вашему случаю, но все же соответствовало случаю, когда кто-то просто забыл std::forward ? Как заявляли другие, руководство направлено не на исключение любого кода, который не соответствует этому руководству, а на то, чтобы помочь диагностировать распространенные ошибки с помощью руководства (а это обязательно приводит к ложным срабатываниям).

463035818_is_not_an_ai 23.07.2024 11:19

другими словами, хорошее руководство — это такое, которое с высокой вероятностью идентифицирует неправильный фрагмент кода. Это фокус. Наличие другого неправильного кода, который не указан в руководстве, неизбежно, а наличие правильного кода, который указан в руководстве, является компромиссом. Неработающий код обычно обходится намного дороже, чем программист, который проверяет свой код, чтобы убедиться, что он «в порядке», они все равно должны были это сделать;)

463035818_is_not_an_ai 23.07.2024 14:42

@n.m.couldbeanAI Я уверен, что он не скомпилируется, потому что для краткости я пропустил некоторые вещи (например, #includes). Это не должно было быть MRE, а скорее псевдокодом, передающим какую-то мысль. Могу ли я внести ясность в вопрос, исправив опечатки или что-то подобное?

pasbi 24.07.2024 09:49
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
7
121
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Да.

Возможно, проблема здесь в том, что ссылки на пересылку изначально были введены для одной конкретной цели: пересылки. Поэтому в итоге мы им позвонили пересылали рекомендации. В этом контексте, безусловно, имеет смысл задаться вопросом об использовании ссылки пересылки, которая не пересылается. И действительно, это, безусловно, наиболее распространенный вариант использования ссылок на пересылку.

Однако ссылки пересылки также служат другой цели — еще одной встроенной в их первоначальное имя: универсальной ссылке. Если вы сегодня хотите написать функцию, которая может принимать либо lvalue, либо rvalue, у вас есть только три варианта:

  • template <class T> void f(T a); — это может быть то, что вы хотите, но для этого требуется копирование lvalue и принудительное перемещение значений x.
  • template <class T> void g(T const& b); — это хороший выбор по умолчанию, перемещение или копирование не происходит, но b всегда есть const
  • template <class T> void h(T&& c); — перемещение или копирование не происходит, c может быть изменчивым.

Если вы хотите избежать копирования/перемещения и вам необходимо сохранить const-ность, единственный вариант — использовать ссылку пересылки. Что в данном контексте лучше назвать универсальной ссылкой. Но в любом случае это один и тот же синтаксис. Диапазоны активно используют этот паттерн – и в этом паттерне нет ничего плохого. Это инструмент, доступный в нашем распоряжении для решения этой проблемы.

Вы можете себе представить, что, возможно, если бы у нас было что-то вроде метки параметра, мы могли бы различать контексты, в которых мы просто хотим иметь универсальную ссылку, и где мы хотим фактически пересылать данные. Но за исключением чего-то подобного, вариант использования «универсальной ссылки» для пересылки ссылок вполне допустим. Мы просто не можем синтаксически отличить случаи, когда мы намеревались пересылать, но забыли это сделать (которые следует пометить), и случаи, когда мы не собирались пересылать и не должны (которые не следует помечать).

Другие вопросы по теме