std::forward_like
| Определено в заголовочном файле <utility>
|
||
template< class T, class U > [[nodiscard]] constexpr auto&& forward_like( U&& x ) noexcept; |
(начиная с C++23) | |
Возвращает ссылку на x, свойства которого аналогичны T&&.
Возвращаемый тип определяется следующим образом:
- Если
std::remove_reference_t<T>является const-квалифицированным типом, тогда ссылочный тип возвращаемого типа являетсяconst std::remove_reference_t<U>. Иначе ссылочным типом являетсяstd::remove_reference_t<U>. - Если
T&&является левосторонним ссылочным типом, то возвращаемый тип также является левосторонним ссылочным типом. В противном случае возвращаемый тип является правосторонним ссылочным типом.
Программа некорректна, если T&& не является допустимым типом.
Параметры
| x | — | значение, которое должно быть перенаправлено как тип T
|
Возвращаемое значение
Ссылка на x типа, определённого выше.
Примечание
Подобно std::forward, std::move и std::as_const, std::forward_like это приведение типа, которое влияет только на категорию значений выражения или потенциально добавляет const-квалификацию.
Когда m является фактическим элементом и, следовательно, o.m допустимое выражение, в коде C++20 это обычно пишется как std::forward<decltype(o)>(o).m.
Когда o.m не является допустимым выражением, т.е. элементами лямбда-замыканий, требуется std::forward_like</*смотрите ниже*/>(m).
Это приводит к трем возможным моделям, называемым слияние, кортеж и язык.
- слияние: объединяет квалификаторы
constи применяет категорию значенийOwner. - кортеж: что делает
std::get<0>(Owner), предполагая, чтоOwnerявляетсяstd::tuple<Member>. - язык: что делает
std::forward<decltype(Owner)>(o).m.
Основной сценарий, который обслуживает std::forward_like это адаптация “дальних” объектов. Ни кортеж, ни язык не подходят для этого основного варианта использования, поэтому для std::forward_like используется модель слияние.
| Макрос Тестирования функциональности | Значение | Стандарт | Функциональность |
|---|---|---|---|
__cpp_lib_forward_like |
202207L |
(C++23) | std::forward_like
|
Возможная реализация
template<class T, class U>
[[nodiscard]] constexpr auto&& forward_like(U&& x) noexcept
{
constexpr bool is_adding_const = std::is_const_v<std::remove_reference_t<T>>;
if constexpr (std::is_lvalue_reference_v<T&&>)
{
if constexpr (is_adding_const)
return std::as_const(x);
else
return static_cast<U&>(x);
}
else
{
if constexpr (is_adding_const)
return std::move(std::as_const(x));
else
return std::move(x);
}
}
|
Пример
#include <cstddef>
#include <iostream>
#include <memory>
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>
struct TypeTeller
{
void operator()(this auto&& self)
{
using SelfType = decltype(self);
using UnrefSelfType = std::remove_reference_t<SelfType>;
if constexpr (std::is_lvalue_reference_v<SelfType>)
{
if constexpr (std::is_const_v<UnrefSelfType>)
std::cout << "const lvalue\n";
else
std::cout << "mutable lvalue\n";
}
else
{
if constexpr (std::is_const_v<UnrefSelfType>)
std::cout << "const rvalue\n";
else
std::cout << "mutable rvalue\n";
}
}
};
struct FarStates
{
std::unique_ptr<TypeTeller> ptr;
std::optional<TypeTeller> opt;
std::vector<TypeTeller> container;
auto&& from_opt(this auto&& self)
{
return std::forward_like<decltype(self)>(self.opt.value());
// Можно использовать std::forward<decltype(self)>(self).opt.value(),
// потому что std::optional предоставляет подходящие методы доступа.
}
auto&& operator[](this auto&& self, std::size_t i)
{
return std::forward_like<decltype(self)>(container.at(i));
// Не очень хорошо использовать std::forward<decltype(self)>(self)[i], потому
// что контейнеры не предоставляют доступ по индексу rvalue, хотя могли бы.
}
auto&& from_ptr(this auto&& self)
{
if (!self.ptr)
throw std::bad_optional_access{};
return std::forward_like<decltype(self)>(*self.ptr);
// Нехорошо использовать *std::forward<decltype(self)>(self).ptr, потому что
// std::unique_ptr<TypeTeller> всегда разыменовывает неконстантное lvalue.
}
};
int main()
{
FarStates my_state{
.ptr{std::make_unique<TypeTeller>()},
.opt{std::in_place, TypeTeller{} },
.container{std::vector<TypeTeller>(1)},
};
my_state.from_ptr();
my_state.from_opt();
my_state[0]();
std::cout << '\n';
std::as_const(my_state).from_ptr();
std::as_const(my_state).from_opt();
std::as_const(my_state)[0]();
std::cout << '\n';
std::move(my_state).from_ptr();
std::move(my_state).from_opt();
std::move(my_state)[0]();
std::cout << '\n';
std::move(std::as_const(my_state)).from_ptr();
std::move(std::as_const(my_state)).from_opt();
std::move(std::as_const(my_state))[0]();
std::cout << '\n';
}
Вывод:
mutable lvalue
mutable lvalue
mutable lvalue
const lvalue
const lvalue
const lvalue
mutable rvalue
mutable rvalue
mutable rvalue
const rvalue
const rvalue
const rvalue
Смотрите также
(C++11) |
получает ссылку на rvalue (шаблон функции) |
(C++11) |
пересылает аргумент функции (шаблон функции) |
(C++17) |
получает ссылку на константу её аргумента (шаблон функции) |