Вторая перегрузка std::foward (пример на cppreference.com)

avatar
Ruperrrt
4 августа 2021 в 13:07
207
1
4

Я знаю, что вторая перегрузка std::forward:

template< class T >
constexpr T&& forward( std::remove_reference_t<T>&& t ) noexcept;

используется для rvalue (как заявил Говард Хиннант в своем ответе: Как std::forward получает правильный аргумент?)

Есть пример использования этой перегрузки на cppreference.com (это также упоминается в Как std::forward получает правильный аргумент? от Praetorian):

  1. Пересылает rvalue как rvalue и запрещает переадресацию rvalue как lvalue. Эта перегрузка позволяет пересылать результат выражения (например, вызова функции), который может быть rvalue или lvalue, в качестве категории исходного значения аргумента пересылаемой ссылки .

Например, если оболочка не просто пересылает свой аргумент, но вызывает функцию-член для аргумента и пересылает свой результат:

// transforming wrapper 
template<class T>
void wrapper(T&& arg)
{
    foo(forward<decltype(forward<T>(arg).get())>(forward<T>(arg).get()));
}

где тип arg может быть

struct Arg
{
    int i = 1;
    int  get() && { return i; } // call to this overload is rvalue
    int& get() &  { return i; } // call to this overload is lvalue
};

Я действительно не понимаю этот пример. Зачем вообще нужен внешний форвард forward<decltype(forward<T>(arg).get())>?

Состояния Cpppreference:

Эта перегрузка позволяет пересылать результат выражения (например, вызова функции), который может быть rvalue или lvalue, в качестве категории исходного значения аргумента ссылки пересылки.

Например:

void func(int& lvalue)
{
    std::cout << "I got an lvalue!" << std::endl;
}

void func(int&& rvalue)
{
    std::cout << "I got an rvalue!" << std::endl;
}

template <typename T>
T&& myForward(typename std::remove_reference_t<T>& t)
{
    return static_cast<T&&>(t);
}

struct foo
{
    int i = 42;
    int& get()& { return i; }
    int get()&& { return i; }
};

template <typename T>
void wrapper(T&& t)
{

    func(myForward<T>(t).get());
}

int main()
{
    foo f;
    wrapper(f);
    wrapper(foo());

    return 0;
}

Это печатает:

I got an lvalue!
I got an rvalue!

просто отлично, без внешней переадресации, в то время как он также пересылает «результат выражения [...] как исходную категорию значения аргумента пересылаемой ссылки». Ему даже не нужна вторая перегрузка std::forward. Эта перегрузка необходима только при вызове func() следующим образом:

func(myForward<decltype(myForward<T>(t).get())>(myForward<T>(t).get()));

Тем не менее, я не могу понять, зачем кому-то нужно добавлять внешний форвард.

Изменить: Изменить перемещено в последующий вопрос: Перегрузка RValue-reference std::forward потенциально может привести к висячей ссылке?

Источник
Yakk - Adam Nevraumont
4 августа 2021 в 13:42
0

Вы цитируете какой-то неизвестный источник. Поиск ссылки над цитатой, содержащей «пример, если оболочка не работает» для этой строки, не показывает эту строку? Предполагается ли, что приведенный выше раздел «в кавычках» является цитатой или он просто неправильно отформатирован?

Yakk - Adam Nevraumont
4 августа 2021 в 13:44
0

Вы перефразируете, делая вид, что цитируете? Я запутался? Вы ошиблись ссылкой?

Yakk - Adam Nevraumont
4 августа 2021 в 13:45
0

Ах, вы цитируете en.cppreference.com/w/cpp/utility/forward

dewaffled
4 августа 2021 в 14:10
0

Вероятно, это просто пример того, что может произойти в сложных сценариях программирования шаблонов. Чтобы упростить еще больше: мы, вероятно, хотим, чтобы код вида myForward<int>(42); компилировался, и он не будет компилироваться без перегрузки std::remove_reference_t<T>&& t.

xskxzr
5 августа 2021 в 06:14
2

Дубликат: coderhelper.com/q/41543571/5376789

Ответы (1)

avatar
StoryTeller - Unslander Monica
4 августа 2021 в 15:58
2

Зачем вообще нужен внешний форвард forward<decltype(forward<T>(arg).get())>?

Это не. Выражение уже имеет собственную правильную категорию значений. В С++ 17 (при возврате по значению больших типов) это даже пессимизация. Все, что он делает, это превращает потенциальное значение prvalue в значение x, и запрещает удаление копии. Мне так и хочется сказать, что это карго-культ программирования.

Ruperrrt
5 августа 2021 в 10:47
0

Спасибо за ваш ответ! Это заставило меня поближе взглянуть на категории значений, включенные в полное выражение. Следовательно, мне интересно, может ли вторая перегрузка std::forward вызвать висячие ссылки при использовании с prvalues ​​(см. мое редактирование).

StoryTeller - Unslander Monica
5 августа 2021 в 10:54
0

@Ruperrrt - пожалуйста, прочитайте meta.stackexchange.com/q/93513/317773

Ruperrrt
5 августа 2021 в 13:05
0

Спасибо, что подчеркнули это! Переместил редактирование в дополнительный вопрос.