std::ranges...:single_view(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’œ std::ranges::views::single, std::ranges::single_view πŸ’œ

okay bestie, welcome to the <ranges> glow-up β€” this is the C++ Standard Library page for std::ranges::views::single and std::ranges::single_view, and honestly? it's giving one-element-legend. one (1) element. that's the whole personality. and it slays.

🌸 Synopsis 🌸

defined in header <ranges>, obviously πŸ’…

template< std::copy_constructible T >
    requires std::is_object_v<T>
class single_view

(since C++20) (until C++23)

    : public ranges::view_interface<single_view<T>>
template< std::move_constructible T >
    requires std::is_object_v<T>
class single_view

(since C++23)

    : public ranges::view_interface<single_view<T>>            (1)
namespace views {
    inline constexpr /*unspecified*/ single = /*unspecified*/;

(2) (since C++20)

}

πŸŽ€ Call signature πŸŽ€

template< class T >
    requires /* see below */

(since C++20)

constexpr /*see below*/ single( T&& t );

so like, what do these actually do:

  1. produces a view that contains exactly one element of a specified value. one. uno. 🫢
  2. the expression views::single(e) is expression-equivalent to single_view<std::decay_t<decltype((e))>>(e) for any suitable subexpression e.
bestie pls note πŸ“ the lifetime of the element is bound to the parent single_view. copying single_view makes a copy of the element. no cap.

✨ Member functions ✨

the main-character lineup:

  • constructor β€” constructs a single_view (C++20) (public member function)
  • begin β€” returns a pointer to the element (C++20) (public member function)
  • end β€” returns a pointer past the element (C++20) (public member function)
  • size β€” returns 1 (one) [static] (C++20) (public static member function)
  • data β€” returns a pointer to the element (C++20) (public member function)

πŸ¦‹ Inherited from std::ranges::view_interface πŸ¦‹

these are the inherited besties, they came free with the fit:

  • empty β€” returns whether the derived view is empty. provided if it satisfies sized_range or forward_range. (C++20) (public member function of std::ranges::view_interface<D>)
  • cbegin β€” returns a constant iterator to the beginning of the range. (C++23) (public member function of std::ranges::view_interface<D>)
  • cend β€” returns a sentinel for the constant iterator of the range. (C++23) (public member function of std::ranges::view_interface<D>)
  • operator bool β€” returns whether the derived view is not empty. provided if ranges::empty is applicable to it. (C++20) (public member function of std::ranges::view_interface<D>)
  • front β€” returns the first element in the derived view. provided if it satisfies forward_range. (C++20) (public member function of std::ranges::view_interface<D>)
  • back β€” returns the last element in the derived view. provided if it satisfies bidirectional_range and common_range. (C++20) (public member function of std::ranges::view_interface<D>)
  • operator[] β€” returns the n^th element in the derived view. provided if it satisfies random_access_range. (C++20) (public member function of std::ranges::view_interface<D>)

πŸ’… std::ranges::single_view::single_view πŸ’…

the constructors, aka the four ways to be that view:

single_view() requires std::default_initializable<T>   (1) (since C++20)
= default;
constexpr explicit single_view( const T& t );

(since C++20) (until C++23)

constexpr explicit single_view( const T& t )

(since C++23)

    requires std::copy_constructible<T>;
constexpr explicit single_view( T&& t );                 (3)

(since C++20)

template< class... Args >                            (2)
    requires std::constructible_from<T, Args...>         (4)

(since C++20)

constexpr explicit single_view( std::in_place_t,
Args&&... args );

constructs a single_view. that's it. that's the job. 😌

  1. default initializes value_, which value-initializes its contained value.
  2. initializes value_ with t.
  3. initializes value_ with std::move(t).
  4. initializes value_ as if by value_{std::in_place, std::forward<Args>(args)...}.

🫢 std::ranges::single_view::begin 🫢

constexpr T* begin() noexcept;              (since C++20)
constexpr const T* begin() const noexcept;

equivalent to return data();. iconic, minimal, no notes. ✨

🫢 std::ranges::single_view::end 🫢

constexpr T* end() noexcept;              (since C++20)
constexpr const T* end() const noexcept;

equivalent to return data() + 1;. yes, + 1. it's one element, babe, the math is mathing. πŸŽ€

🫢 std::ranges::single_view::size 🫢

static constexpr std::size_t size() noexcept;  (since C++20)

equivalent to return 1;. πŸ’€ that's literally the whole implementation and i love that for us.

this is a static function. this makes single_view model /*tiny-range*/ as required by split_view. iykyk. 🌈

🫢 std::ranges::single_view::data 🫢

constexpr T* data() noexcept;              (since C++20)
constexpr const T* data() const noexcept;

returns a pointer to the contained value of value_.

⚠️ bestie, warning ⚠️ the behavior is undefined if value_ does not contains a value. yes, "does not contains" β€” we're quoting the manpage energy here, and also: don't do it. don't test it. πŸ™ˆ

⭐ Deduction guides ⭐

template< class T >                  (since C++20)
single_view( T ) -> single_view<T>;

🌸 Notes 🌸

for a single_view, the inherited empty member function always returns false, and the inherited operator bool conversion function always returns true.

which, honestly, same. main character energy: never empty, always valid. πŸ‘‘πŸ’œ


πŸ”₯ Example πŸ”₯

// Run this code

#include <iomanip>
#include <iostream>
#include <ranges>
#include <string>
#include <tuple>

int main()
{
    constexpr std::ranges::single_view sv1{3.1415}; // uses (const T&) constructor
    static_assert(sv1);
    static_assert(not sv1.empty());

    std::cout << "1) *sv1.data(): " << *sv1.data() << '\n'
              << "2) *sv1.begin(): " << *sv1.begin() << '\n'
              << "3)  sv1.size(): " << sv1.size() << '\n'
              << "4)  distance: " << std::distance(sv1.begin(), sv1.end()) << '\n';

    std::string str{"C++20"};
    std::cout << "5)  str = " << std::quoted(str) << '\n';
    std::ranges::single_view sv2{std::move(str)}; // uses (T&&) constructor
    std::cout << "6) *sv2.data(): " << std::quoted(*sv2.data()) << '\n'
              << "7)  str = " << std::quoted(str) << '\n';

    std::ranges::single_view<std::tuple<int, double, std::string>>
        sv3{std::in_place, 42, 3.14, "πŸ˜„"}; // uses (std::in_place_t, Args&&... args)

    std::cout << "8)  sv3 holds a tuple: { "
              << std::get<0>(sv3[0]) << ", "
              << std::get<1>(sv3[0]) << ", "
              << std::get<2>(sv3[0]) << " }\n";
}

πŸ“ Output πŸ“

1) *sv1.data(): 3.1415
2) *sv1.begin(): 3.1415
3)  sv1.size(): 1
4)  distance: 1
5)  str = "C++20"
6) *sv2.data(): "C++20"
7)  str = ""
8)  sv3 holds a tuple: { 42, 3.14, πŸ˜„ }
side note from the group chat: line 7 is lowkey devastating β€” str got moved-from and is now empty, that's not a bug that's just move semantics doing its thing. 😭✨

🧚 Defect reports 🧚

the following behavior-changing defect reports were applied retroactively to previously published C++ standards. yes, they went back and edited history. ate. πŸ’…

  • LWG 3428 β€” applied to C++20. behavior as published: single_view was convertible from std::in_place_t; deduction guides for single_view failed to decay the argument; views::single copied but not wrapped a single_view. correct behavior: the constructor is made explicit; a decaying guide provided; made always wrapping. πŸ”₯
  • P2367R0 β€” applied to C++20. behavior as published: single_view was convertible from std::in_place_t; deduction guides for single_view failed to decay the argument; views::single copied but not wrapped a single_view. correct behavior: the constructor is made explicit; a decaying guide provided; made always wrapping. πŸ”₯

🌈 See also 🌈

  • ranges::empty_view, views::empty (C++20) β€” an empty view with no elements (class template) (variable template)
  • ranges::split_view, views::split (C++20) β€” a view over the subranges obtained from splitting another view using a delimiter (class template) (range adaptor object)

okay that's the whole page, bestie, you're officially a single_view scholar πŸŽ“πŸ’– no cap. this was pulled from http://cppreference.com, version 2024.06.10, filed under std::ranges...:single_view(3) β€” go forth and hold exactly one (1) element, periodt. πŸ¦‹πŸ‘‘

πŸ“¦ where this page comes from