Универсальные ссылки: почему при выводе этого нет std::forward?

Я смотрел видео Скотта Мейерса об универсальных ссылках, и он рассказал, что каждый раз, когда вы используете универсальную ссылку, вы должны пересылать ее так:

template<typename T>
auto func(T &&a) { return ++std::forward<T>(a); }

Тогда, выведя this, мы имеем:

auto member_func(this auto &&self) { return self.member_variable; }

Где здесь std::forward<???>(self).member_variable? Что я пропустил?

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

Pepijn Kramer 24.08.2024 22:08

Я думаю, это объясняет это Явные функции-члены объекта «...Для шаблонов функций-членов явный параметр объекта позволяет выводить тип и категорию значения, эта языковая функция называется «выводом этого»:...»

Richard Critten 24.08.2024 22:10

Вопрос в том, почему вы не использовали std::forward в этой функции? Я не знаю, почему ты этим не воспользовался.

Barry 24.08.2024 22:14

Скотт Мейес ушел в отставку некоторое время назад. Это очень современный C++, о котором он не мог знать во время своего выступления (о котором вы говорите).

Elliott 24.08.2024 22:14

«должен» — это немного сильно. Вы можете пересылать явный параметр объекта так же, как и любой другой, по тем же причинам или нет.

user17732522 24.08.2024 22:15

@PepijnKramer Функция с явным параметром объекта, использующим auto, является таким же шаблоном функции (члена), как и любой другой. this auto&& — это ссылка на пересылку в сокращенном синтаксисе шаблона функции, как, например, void f(auto&& x) {}.

user17732522 24.08.2024 22:20

Когда ссылка пересылки используется по имени, это lvalue. В любом другом коде, использующем параметр auto&& p, ситуация ничем не отличается: вам нужно std::forward<decltype(p)>(p), чтобы передать ценность.

Gene 24.08.2024 22:31

Я не думаю, что есть какая-либо разница в использовании его в этом примере кода с тривиальными типами. Категория значения возвращаемого значения может измениться, но тип возвращаемого значения — auto, так что изменение в любом случае немедленно теряется, лишь способствуя формированию возвращаемого объекта. Вы можете проверить это, наблюдая за различиями: gcc.godbolt.org/z/e5Eoc9W1r

chris 24.08.2024 22:42
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
8
90
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Обычно ссылку пересылки в явном параметре объекта следует пересылать так же, как и любую другую ссылку пересылки:

auto member_func(this auto &&self) {
    return std::forward<decltype(self)>(self).member_variable;
}

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

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

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

Теперь предположим, что это выглядит так:

struct A {
    int member_variable;
    auto member_func(this auto &&self) { return self.member_variable; }
};

Тогда разумно предположить, что типом self.member_variable является int, и для int совершенно не имеет значения, возвращается ли оно по значению (auto) из выражения lvalue или rvalue. Известно, что вызов std::forward не даст никакого эффекта.

Проблемы с этим:

  1. Функция-член не ограничена типом класса, в котором она сама появляется. Если тип класса не отмечен final или объявлена ​​функция-член private, то вполне возможно, что она будет вызвана для типа производного класса, который также определяет member_variable другого типа. Это будет найдено для return self.member_variable; и может иметь тип, при котором пересылка может иметь значение, например. std::string.

    Однако для меня не очевидно, рассматривал ли автор кода этот сценарий и действительно ли он хочет всегда возвращать member_variable самого класса. В этом случае это должно быть self.A::member_variable или что-то в этом роде. В этом случае все еще остаются проблемы с множественным и частным наследованием. (Однако существует решение для частного наследования.)

  2. Если ссылка пересылки не пересылается, то в этом случае вообще нет смысла указывать явный параметр объекта this auto&&. Его можно было бы просто объявить как this const auto& или this const A&, что также позволит избежать упомянутой выше проблемы. На самом деле тогда это может быть просто обычная нестатическая функция-член без явного параметра объекта.

  3. Поскольку указатели функций на явные функции-члены могут быть сформированы, можно вызвать функцию-член косвенно через такой указатель с любым другим типом для self. Аналогично, неявное преобразование к любому типу можно вызвать, явно указав аргумент шаблона для типа auto. Однако это не обычные варианты использования, и функции, вероятно, не нужно их учитывать.


Кроме того, в целом важно помнить, что означает использование std::forward. Наивное применение std::forward к любому появлению переменной, которая является ссылкой на пересылку, неверно. std::forward заставляет выражение, для которого он является операндом (потенциально) использовать состояние объекта. Обычно его можно применить только один раз в теле функции, после чего объект больше не следует использовать.

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

return std::forward<decltype(self)>(self).member_variable;

это немного по-другому: здесь std::forward вызывает потенциальное потребление только участника member_variable. Остальные члены гарантированно останутся в своем предыдущем состоянии. Таким образом, можно подать заявку std::forward один раз для каждого участника индивидуально.

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