C++20stladvanced
std::unwrap_reference / std::unwrap_ref_decay (C++20 Addition)
C++20 · The Big Four · Compiler flag: -std=c++20
Explanation
Type traits that unwrap `std::reference_wrapper<T>` to `T&`, and leave other types unchanged. `unwrap_ref_decay` first applies `std::decay_t` before unwrapping. Useful in generic code that stores or forwards arguments which might be reference-wrapped.
Real-World Scenario
Implement a `make_tuple`-like function that correctly handles `std::ref` arguments.
Quick Snippet
snippet.cpp
#include <functional>
#include <type_traits>
template<typename T>
void store(T&& val) {
using Stored = std::unwrap_ref_decay_t<T>;
// If T is reference_wrapper<int>, Stored is int&
// If T is int, Stored is int
}
int x = 42;
store(std::ref(x)); // Stored deduces to int&
store(42); // Stored deduces to intFull Example
unwrap_reference.cpp
// CodeNebula - C++20 Example: unwrap_reference and unwrap_ref_decay
// Compile: g++ -std=c++20 -Wall -Wextra -o example unwrap_reference.cpp
#include <functional>
#include <iostream>
#include <type_traits>
// unwrap_reference<T>::type unwraps reference_wrapper<U> to U&, leaves others as T
// unwrap_ref_decay<T>::type applies decay first, then unwrap_reference
template <typename T>
void show_unwrapped_type(T&& val) {
using original = decltype(val);
using unwrapped = typename std::unwrap_reference<std::remove_cvref_t<original>>::type;
std::cout << " Value: " << val << "\n";
std::cout << " Is reference_wrapper: "
<< (!std::is_same_v<std::remove_cvref_t<original>, unwrapped> ? "yes" : "no")
<< "\n";
}
template <typename T>
void demonstrate_unwrap_ref_decay() {
using decayed = std::unwrap_ref_decay_t<T>;
std::cout << " unwrap_ref_decay applied\n";
std::cout << " Same as original after decay: "
<< std::is_same_v<std::decay_t<T>, decayed> << "\n";
}
int main() {
std::cout << "=== std::unwrap_reference ===\n";
int x = 42;
auto ref = std::ref(x);
auto cref = std::cref(x);
// unwrap_reference on reference_wrapper yields int&
using T1 = std::unwrap_reference_t<decltype(ref)>;
static_assert(std::is_same_v<T1, int&>);
std::cout << "ref(x) unwraps to int&: true\n";
// unwrap_reference on reference_wrapper<const int> yields const int&
using T2 = std::unwrap_reference_t<decltype(cref)>;
static_assert(std::is_same_v<T2, const int&>);
std::cout << "cref(x) unwraps to const int&: true\n";
// Plain types are left unchanged
using T3 = std::unwrap_reference_t<int>;
static_assert(std::is_same_v<T3, int>);
std::cout << "int remains int: true\n";
std::cout << "\n=== std::unwrap_ref_decay ===\n";
// Decays array, then checks for reference_wrapper
using T4 = std::unwrap_ref_decay_t<int[5]>;
static_assert(std::is_same_v<T4, int*>);
std::cout << "int[5] decays to int*: true\n";
// Decays const ref, then checks
using T5 = std::unwrap_ref_decay_t<const int&>;
static_assert(std::is_same_v<T5, int>);
std::cout << "const int& decays to int: true\n";
// reference_wrapper through decay
using T6 = std::unwrap_ref_decay_t<std::reference_wrapper<int>>;
static_assert(std::is_same_v<T6, int&>);
std::cout << "reference_wrapper<int> unwraps to int&: true\n";
std::cout << "\n=== Practical use with make_pair-like function ===\n";
show_unwrapped_type(ref);
show_unwrapped_type(100);
return 0;
}
Run command
g++ -std=c++20 -Wall -Wextra -o example unwrap_reference.cpp