Для tuple-like протоколу structured binding зазвичай пишуть чотири get: &, const &, &&, const &&. У C++23 deducing this дозволяє замінити їх одним шаблонним методом, а категорію значення поля прокинути через std::forward_like (<utility>).
Реалізація
#include <cstddef>
#include <string>
#include <utility>
class User {
private:
int id_;
std::string name_;
public:
User(int id, std::string name)
: id_(id), name_(std::move(name)) {}
template <std::size_t Index, typename Self>
constexpr decltype(auto) get(this Self&& self) noexcept {
if constexpr (Index == 0) {
return std::forward_like<Self>(self.id_);
} else if constexpr (Index == 1) {
return std::forward_like<Self>(self.name_);
}
}
};
namespace std {
template <>
struct tuple_size<User> : std::integral_constant<std::size_t, 2> {};
template <> struct tuple_element<0, User> { using type = int; };
template <> struct tuple_element<1, User> { using type = std::string; };
}
tuple_size / tuple_element лишаються як раніше.
Як виводиться Self
Параметр this Self&& self — універсальне посилання на об’єкт виклику:
| Виклик на | Self |
|---|---|
User& |
User& |
const User& |
const User& |
тимчасовий User&& |
User (далі Self&& → User&&) |
Навіщо std::forward_like
std::forward<Self>(self) перенаправляє сам self. Потрібно перенаправити поле (self.id_, self.name_) з тією ж константністю й категорією значення, що й у Self.
std::forward_like<Self>(field) саме це й робить:
Self |
результат для std::string поля |
|---|---|
User& |
std::string& |
const User& |
const std::string& |
User / User&& |
std::string&& |
Порівняння з ручними перевантаженнями: замість чотирьох методів і ручних std::move / (field) — один шаблон і forward_like.
Детальніше про std::forward_like і заміну CRTP через deducing this — у наступних нотатках.