![](/user_photo/_userpic.png)
- •Contents
- •Preface
- •An Experiment
- •Acknowledgments
- •Versions of this Book
- •About This Book
- •What You Should Know Before Reading This Book
- •Overall Structure of the Book
- •The Way I Implement
- •Initializations
- •Error Terminology
- •The C++ Standards
- •Example Code and Additional Information
- •1 Comparisons and Operator <=>
- •1.1.1 Defining Comparison Operators Before C++20
- •1.1.2 Defining Comparison Operators Since C++20
- •1.2 Defining and Using Comparisons
- •1.2.2 Comparison Category Types
- •1.2.4 Calling Operator <=> Directly
- •1.2.5 Dealing with Multiple Ordering Criteria
- •1.3 Defining operator<=> and operator==
- •1.3.1 Defaulted operator== and operator<=>
- •1.3.2 Defaulted operator<=> Implies Defaulted operator==
- •1.3.3 Implementation of the Defaulted operator<=>
- •1.4 Overload Resolution with Rewritten Expressions
- •1.5 Using Operator <=> in Generic Code
- •1.5.1 compare_three_way
- •1.6 Compatibility Issues with the Comparison Operators
- •1.6.2 Inheritance with Protected Members
- •1.7 Afternotes
- •2 Placeholder Types for Function Parameters
- •2.1.1 auto for Parameters of Member Functions
- •2.2 Using auto for Parameters in Practice
- •2.3.1 Basic Constraints for auto Parameters
- •2.4 Afternotes
- •3.1 Motivating Example of Concepts and Requirements
- •3.1.1 Improving the Template Step by Step
- •3.1.2 A Complete Example with Concepts
- •3.2 Where Constraints and Concepts Can Be Used
- •3.2.1 Constraining Alias Templates
- •3.2.2 Constraining Variable Templates
- •3.2.3 Constraining Member Functions
- •3.3 Typical Applications of Concepts and Constraints in Practice
- •3.3.1 Using Concepts to Understand Code and Error Messages
- •3.3.2 Using Concepts to Disable Generic Code
- •3.3.3 Using Requirements to Call Different Functions
- •3.3.4 The Example as a Whole
- •3.3.5 Former Workarounds
- •3.4 Semantic Constraints
- •3.4.1 Examples of Semantic Constraints
- •3.5 Design Guidelines for Concepts
- •3.5.1 Concepts Should Group Requirements
- •3.5.2 Define Concepts with Care
- •3.5.3 Concepts versus Type Traits and Boolean Expressions
- •3.6 Afternotes
- •4.1 Constraints
- •4.2.1 Using && and || in requires Clauses
- •4.3 Ad-hoc Boolean Expressions
- •4.4.1 Simple Requirements
- •4.4.2 Type Requirements
- •4.4.3 Compound Requirements
- •4.4.4 Nested Requirements
- •4.5 Concepts in Detail
- •4.5.1 Defining Concepts
- •4.5.2 Special Abilities of Concepts
- •4.5.3 Concepts for Non-Type Template Parameters
- •4.6 Using Concepts as Type Constraints
- •4.7 Subsuming Constraints with Concepts
- •4.7.1 Indirect Subsumptions
- •4.7.2 Defining Commutative Concepts
- •5 Standard Concepts in Detail
- •5.1 Overview of All Standard Concepts
- •5.1.1 Header Files and Namespaces
- •5.1.2 Standard Concepts Subsume
- •5.2 Language-Related Concepts
- •5.2.1 Arithmetic Concepts
- •5.2.2 Object Concepts
- •5.2.3 Concepts for Relationships between Types
- •5.2.4 Comparison Concepts
- •5.3 Concepts for Iterators and Ranges
- •5.3.1 Concepts for Ranges and Views
- •5.3.2 Concepts for Pointer-Like Objects
- •5.3.3 Concepts for Iterators
- •5.3.4 Iterator Concepts for Algorithms
- •5.4 Concepts for Callables
- •5.4.1 Basic Concepts for Callables
- •5.4.2 Concepts for Callables Used by Iterators
- •5.5 Auxiliary Concepts
- •5.5.1 Concepts for Specific Type Attributes
- •5.5.2 Concepts for Incrementable Types
- •6 Ranges and Views
- •6.1 A Tour of Ranges and Views Using Examples
- •6.1.1 Passing Containers to Algorithms as Ranges
- •6.1.2 Constraints and Utilities for Ranges
- •6.1.3 Views
- •6.1.4 Sentinels
- •6.1.5 Range Definitions with Sentinels and Counts
- •6.1.6 Projections
- •6.1.7 Utilities for Implementing Code for Ranges
- •6.1.8 Limitations and Drawbacks of Ranges
- •6.2 Borrowed Iterators and Ranges
- •6.2.1 Borrowed Iterators
- •6.2.2 Borrowed Ranges
- •6.3 Using Views
- •6.3.1 Views on Ranges
- •6.3.2 Lazy Evaluation
- •6.3.3 Caching in Views
- •6.3.4 Performance Issues with Filters
- •6.4 Views on Ranges That Are Destroyed or Modified
- •6.4.1 Lifetime Dependencies Between Views and Their Ranges
- •6.4.2 Views with Write Access
- •6.4.3 Views on Ranges That Change
- •6.4.4 Copying Views Might Change Behavior
- •6.5 Views and const
- •6.5.1 Generic Code for Both Containers and Views
- •6.5.3 Bringing Back Deep Constness to Views
- •6.6 Summary of All Container Idioms Broken By Views
- •6.7 Afternotes
- •7 Utilities for Ranges and Views
- •7.1 Key Utilities for Using Ranges as Views
- •7.1.1 std::views::all()
- •7.1.2 std::views::counted()
- •7.1.3 std::views::common()
- •7.2 New Iterator Categories
- •7.3 New Iterator and Sentinel Types
- •7.3.1 std::counted_iterator
- •7.3.2 std::common_iterator
- •7.3.3 std::default_sentinel
- •7.3.4 std::unreachable_sentinel
- •7.3.5 std::move_sentinel
- •7.4 New Functions for Dealing with Ranges
- •7.4.1 Functions for Dealing with the Elements of Ranges (and Arrays)
- •7.4.2 Functions for Dealing with Iterators
- •7.4.3 Functions for Swapping and Moving Elements/Values
- •7.4.4 Functions for Comparisons of Values
- •7.5 New Type Functions/Utilities for Dealing with Ranges
- •7.5.1 Generic Types of Ranges
- •7.5.2 Generic Types of Iterators
- •7.5.3 New Functional Types
- •7.5.4 Other New Types for Dealing with Iterators
- •7.6 Range Algorithms
- •7.6.1 Benefits and Restrictions for Range Algorithms
- •7.6.2 Algorithm Overview
- •8 View Types in Detail
- •8.1 Overview of All Views
- •8.1.1 Overview of Wrapping and Generating Views
- •8.1.2 Overview of Adapting Views
- •8.2 Base Class and Namespace of Views
- •8.2.1 Base Class for Views
- •8.2.2 Why Range Adaptors/Factories Have Their Own Namespace
- •8.3 Source Views to External Elements
- •8.3.1 Subrange
- •8.3.2 Ref View
- •8.3.3 Owning View
- •8.3.4 Common View
- •8.4 Generating Views
- •8.4.1 Iota View
- •8.4.2 Single View
- •8.4.3 Empty View
- •8.4.4 IStream View
- •8.4.5 String View
- •8.4.6 Span
- •8.5 Filtering Views
- •8.5.1 Take View
- •8.5.2 Take-While View
- •8.5.3 Drop View
- •8.5.4 Drop-While View
- •8.5.5 Filter View
- •8.6 Transforming Views
- •8.6.1 Transform View
- •8.6.2 Elements View
- •8.6.3 Keys and Values View
- •8.7 Mutating Views
- •8.7.1 Reverse View
- •8.8 Views for Multiple Ranges
- •8.8.1 Split and Lazy-Split View
- •8.8.2 Join View
- •9 Spans
- •9.1 Using Spans
- •9.1.1 Fixed and Dynamic Extent
- •9.1.2 Example Using a Span with a Dynamic Extent
- •9.1.4 Example Using a Span with Fixed Extent
- •9.1.5 Fixed vs. Dynamic Extent
- •9.2 Spans Considered Harmful
- •9.3 Design Aspects of Spans
- •9.3.1 Lifetime Dependencies of Spans
- •9.3.2 Performance of Spans
- •9.3.3 const Correctness of Spans
- •9.3.4 Using Spans as Parameters in Generic Code
- •9.4 Span Operations
- •9.4.1 Span Operations and Member Types Overview
- •9.4.2 Constructors
- •9.5 Afternotes
- •10 Formatted Output
- •10.1 Formatted Output by Example
- •10.1.1 Using std::format()
- •10.1.2 Using std::format_to_n()
- •10.1.3 Using std::format_to()
- •10.1.4 Using std::formatted_size()
- •10.2 Performance of the Formatting Library
- •10.2.1 Using std::vformat() and vformat_to()
- •10.3 Formatted Output in Detail
- •10.3.1 General Format of Format Strings
- •10.3.2 Standard Format Specifiers
- •10.3.3 Width, Precision, and Fill Characters
- •10.3.4 Format/Type Specifiers
- •10.4 Internationalization
- •10.5 Error Handling
- •10.6 User-Defined Formatted Output
- •10.6.1 Basic Formatter API
- •10.6.2 Improved Parsing
- •10.6.3 Using Standard Formatters for User-Defined Formatters
- •10.6.4 Using Standard Formatters for Strings
- •10.7 Afternotes
- •11 Dates and Timezones for <chrono>
- •11.1 Overview by Example
- •11.1.1 Scheduling a Meeting on the 5th of Every Month
- •11.1.2 Scheduling a Meeting on the Last Day of Every Month
- •11.1.3 Scheduling a Meeting Every First Monday
- •11.1.4 Using Different Timezones
- •11.2 Basic Chrono Concepts and Terminology
- •11.3 Basic Chrono Extensions with C++20
- •11.3.1 Duration Types
- •11.3.2 Clocks
- •11.3.3 Timepoint Types
- •11.3.4 Calendrical Types
- •11.3.5 Time Type hh_mm_ss
- •11.3.6 Hours Utilities
- •11.4 I/O with Chrono Types
- •11.4.1 Default Output Formats
- •11.4.2 Formatted Output
- •11.4.3 Locale-Dependent Output
- •11.4.4 Formatted Input
- •11.5 Using the Chrono Extensions in Practice
- •11.5.1 Invalid Dates
- •11.5.2 Dealing with months and years
- •11.5.3 Parsing Timepoints and Durations
- •11.6 Timezones
- •11.6.1 Characteristics of Timezones
- •11.6.2 The IANA Timezone Database
- •11.6.3 Using Timezones
- •11.6.4 Dealing with Timezone Abbreviations
- •11.6.5 Custom Timezones
- •11.7 Clocks in Detail
- •11.7.1 Clocks with a Specified Epoch
- •11.7.2 The Pseudo Clock local_t
- •11.7.3 Dealing with Leap Seconds
- •11.7.4 Conversions between Clocks
- •11.7.5 Dealing with the File Clock
- •11.8 Other New Chrono Features
- •11.9 Afternotes
- •12 std::jthread and Stop Tokens
- •12.1 Motivation for std::jthread
- •12.1.1 The Problem of std::thread
- •12.1.2 Using std::jthread
- •12.1.3 Stop Tokens and Stop Callbacks
- •12.1.4 Stop Tokens and Condition Variables
- •12.2 Stop Sources and Stop Tokens
- •12.2.1 Stop Sources and Stop Tokens in Detail
- •12.2.2 Using Stop Callbacks
- •12.2.3 Constraints and Guarantees of Stop Tokens
- •12.3.1 Using Stop Tokens with std::jthread
- •12.4 Afternotes
- •13 Concurrency Features
- •13.1 Thread Synchronization with Latches and Barriers
- •13.1.1 Latches
- •13.1.2 Barriers
- •13.2 Semaphores
- •13.2.1 Example of Using Counting Semaphores
- •13.2.2 Example of Using Binary Semaphores
- •13.3 Extensions for Atomic Types
- •13.3.1 Atomic References with std::atomic_ref<>
- •13.3.2 Atomic Shared Pointers
- •13.3.3 Atomic Floating-Point Types
- •13.3.4 Thread Synchronization with Atomic Types
- •13.3.5 Extensions for std::atomic_flag
- •13.4 Synchronized Output Streams
- •13.4.1 Motivation for Synchronized Output Streams
- •13.4.2 Using Synchronized Output Streams
- •13.4.3 Using Synchronized Output Streams for Files
- •13.4.4 Using Synchronized Output Streams as Output Streams
- •13.4.5 Synchronized Output Streams in Practice
- •13.5 Afternotes
- •14 Coroutines
- •14.1 What Are Coroutines?
- •14.2 A First Coroutine Example
- •14.2.1 Defining the Coroutine
- •14.2.2 Using the Coroutine
- •14.2.3 Lifetime Issues with Call-by-Reference
- •14.2.4 Coroutines Calling Coroutines
- •14.2.5 Implementing the Coroutine Interface
- •14.2.6 Bootstrapping Interface, Handle, and Promise
- •14.2.7 Memory Management
- •14.3 Coroutines That Yield or Return Values
- •14.3.1 Using co_yield
- •14.3.2 Using co_return
- •14.4 Coroutine Awaitables and Awaiters
- •14.4.1 Awaiters
- •14.4.2 Standard Awaiters
- •14.4.3 Resuming Sub-Coroutines
- •14.4.4 Passing Values From Suspension Back to the Coroutine
- •14.5 Afternotes
- •15 Coroutines in Detail
- •15.1 Coroutine Constraints
- •15.1.1 Coroutine Lambdas
- •15.2 The Coroutine Frame and the Promises
- •15.2.1 How Coroutine Interfaces, Promises, and Awaitables Interact
- •15.3 Coroutine Promises in Detail
- •15.3.1 Mandatory Promise Operations
- •15.3.2 Promise Operations to Return or Yield Values
- •15.3.3 Optional Promise Operations
- •15.4 Coroutine Handles in Detail
- •15.4.1 std::coroutine_handle<void>
- •15.5 Exceptions in Coroutines
- •15.6 Allocating Memory for the Coroutine Frame
- •15.6.1 How Coroutines Allocate Memory
- •15.6.2 Avoiding Heap Memory Allocation
- •15.6.3 get_return_object_on_allocation_failure()
- •15.7 co_await and Awaiters in Detail
- •15.7.1 Details of the Awaiter Interface
- •15.7.2 Letting co_await Update Running Coroutines
- •15.7.3 Symmetric Transfer with Awaiters for Continuation
- •15.8.1 await_transform()
- •15.8.2 operator co_await()
- •15.9 Concurrent Use of Coroutines
- •15.9.1 co_await Coroutines
- •15.9.2 A Thread Pool for Coroutine Tasks
- •15.9.3 What C++ Libraries Will Provide After C++20
- •15.10 Coroutine Traits
- •16 Modules
- •16.1 Motivation for Modules Using a First Example
- •16.1.1 Implementing and Exporting a Module
- •16.1.2 Compiling Module Units
- •16.1.3 Importing and Using a Module
- •16.1.4 Reachable versus Visible
- •16.1.5 Modules and Namespaces
- •16.2 Modules with Multiple Files
- •16.2.1 Module Units
- •16.2.2 Using Implementation Units
- •16.2.3 Internal Partitions
- •16.2.4 Interface Partitions
- •16.2.5 Summary of Splitting Modules into Different Files
- •16.3 Dealing with Modules in Practice
- •16.3.1 Dealing with Module Files with Different Compilers
- •16.3.2 Dealing with Header Files
- •16.4 Modules in Detail
- •16.4.1 Private Module Fragments
- •16.4.2 Module Declaration and Export in Detail
- •16.4.3 Umbrella Modules
- •16.4.4 Module Import in Detail
- •16.4.5 Reachable versus Visible Symbols in Detail
- •16.5 Afternotes
- •17 Lambda Extensions
- •17.1 Generic Lambdas with Template Parameters
- •17.1.1 Using Template Parameters for Generic Lambdas in Practice
- •17.1.2 Explicit Specification of Lambda Template Parameters
- •17.2 Calling the Default Constructor of Lambdas
- •17.4 consteval Lambdas
- •17.5 Changes for Capturing
- •17.5.1 Capturing this and *this
- •17.5.2 Capturing Structured Bindings
- •17.5.3 Capturing Parameter Packs of Variadic Templates
- •17.5.4 Lambdas as Coroutines
- •17.6 Afternotes
- •18 Compile-Time Computing
- •18.1 Keyword constinit
- •18.1.1 Using constinit in Practice
- •18.1.2 How constinit Solves the Static Initialization Order Fiasco
- •18.2.1 A First consteval Example
- •18.2.2 constexpr versus consteval
- •18.2.3 Using consteval in Practice
- •18.2.4 Compile-Time Value versus Compile-Time Context
- •18.4 std::is_constant_evaluated()
- •18.4.1 std::is_constant_evaluated() in Detail
- •18.5 Using Heap Memory, Vectors, and Strings at Compile Time
- •18.5.1 Using Vectors at Compile Time
- •18.5.2 Returning a Collection at Compile Time
- •18.5.3 Using Strings at Compile Time
- •18.6.1 constexpr Language Extensions
- •18.6.2 constexpr Library Extensions
- •18.7 Afternotes
- •19.1 New Types for Non-Type Template Parameters
- •19.1.1 Floating-Point Values as Non-Type Template Parameters
- •19.1.2 Objects as Non-Type Template Parameters
- •19.2 Afternotes
- •20 New Type Traits
- •20.1 New Type Traits for Type Classification
- •20.1.1 is_bounded_array_v<> and is_unbounded_array_v
- •20.2 New Type Traits for Type Inspection
- •20.2.1 is_nothrow_convertible_v<>
- •20.3 New Type Traits for Type Conversion
- •20.3.1 remove_cvref_t<>
- •20.3.2 unwrap_reference<> and unwrap_ref_decay_t
- •20.3.3 common_reference<>_t
- •20.3.4 type_identity_t<>
- •20.4 New Type Traits for Iterators
- •20.4.1 iter_difference_t<>
- •20.4.2 iter_value_t<>
- •20.4.3 iter_reference_t<> and iter_rvalue_reference_t<>
- •20.5 Type Traits and Functions for Layout Compatibility
- •20.5.1 is_layout_compatible_v<>
- •20.5.2 is_pointer_interconvertible_base_of_v<>
- •20.5.3 is_corresponding_member()
- •20.5.4 is_pointer_interconvertible_with_class()
- •20.6 Afternotes
- •21 Small Improvements for the Core Language
- •21.1 Range-Based for Loop with Initialization
- •21.2 using for Enumeration Values
- •21.3 Delegating Enumeration Types to Different Scopes
- •21.4 New Character Type char8_t
- •21.4.2 Broken Backward Compatibility
- •21.5 Improvements for Aggregates
- •21.5.1 Designated Initializers
- •21.5.2 Aggregate Initialization with Parentheses
- •21.5.3 Definition of Aggregates
- •21.6 New Attributes and Attribute Features
- •21.6.1 Attributes [[likely]] and [[unlikely]]
- •21.6.2 Attribute [[no_unique_address]]
- •21.6.3 Attribute [[nodiscard]] with Parameter
- •21.7 Feature Test Macros
- •21.8 Afternotes
- •22 Small Improvements for Generic Programming
- •22.1 Implicit typename for Type Members of Template Parameters
- •22.1.1 Rules for Implicit typename in Detail
- •22.2 Improvements for Aggregates in Generic Code
- •22.2.1 Class Template Argument Deduction (CTAD) for Aggregates
- •22.3.1 Conditional explicit in the Standard Library
- •22.4 Afternotes
- •23 Small Improvements for the C++ Standard Library
- •23.1 Updates for String Types
- •23.1.1 String Members starts_with() and ends_with()
- •23.1.2 Restricted String Member reserve()
- •23.2 std::source_location
- •23.3 Safe Comparisons of Integral Values and Sizes
- •23.3.1 Safe Comparisons of Integral Values
- •23.3.2 ssize()
- •23.4 Mathematical Constants
- •23.5 Utilities for Dealing with Bits
- •23.5.1 Bit Operations
- •23.5.2 std::bit_cast<>()
- •23.5.3 std::endian
- •23.6 <version>
- •23.7 Extensions for Algorithms
- •23.7.1 Range Support
- •23.7.2 New Algorithms
- •23.7.3 unseq Execution Policy for Algorithms
- •23.8 Afternotes
- •24 Deprecated and Removed Features
- •24.1 Deprecated and Removed Core Language Features
- •24.2 Deprecated and Removed Library Features
- •24.2.1 Deprecated Library Features
- •24.2.2 Removed Library Features
- •24.3 Afternotes
- •Glossary
- •Index
![](/html/75672/2303/html_Pld0VJ4wzF.QW__/htmlconvd-7poybx338x1.jpg)
312 Chapter 9: Spans
Using Spans as Ranges and Views
Because spans are views, they satisfy the concept std::ranges::view.3 This means that spans can be used in all algorithms and functions for ranges and views. In the chapter that discusses the details of all views, we list the view-specific properties of spans.
One property of spans is that they are borrowed ranges, meaning that the lifetime of iterators does not depend on the lifetime of the span. For that reason, we can use a temporary span as a range in algorithms that yield iterators to it:
std::vector<int> coll{25, 42, 2, 0, 122, 5, 7};
auto pos1 = std::ranges::find(std::span{coll.data(), 3}, 42); // OK std::cout << *pos1 << '\n';
However, note that it is an error if the span refers to a temporary object. The following code compiles even though an iterator to a destroyed temporary object is returned:
auto pos2 |
= std::ranges::find(std::span{getData().data(), 3}, 42); |
|
std::cout |
<< *pos2 << '\n'; |
// runtime ERROR |
9.4Span Operations
This section describes the types and operations of spans in detail.
9.4.1 Span Operations and Member Types Overview
Table Span operations lists all operations that are provided for spans.
It is worth noting all the operations that are not supported:
•Comparisons (not even ==)
•swap()
•assign()
•at()
•I/O operators
•cbegin(), cend(), crbegin(), crend()
•Hashing
•Tuple-like API for structured bindings
That is, spans are neither containers (in the traditional C++ STL sense) nor regular types.
Regarding static members and member types, spans provide the usual members of containers (except const_iterator) plus two special ones: element_type and extent (see table Static and type members of spans).
Note that std::value_type is not the specified element type (as the value_type for std::array and several other types usually is). It is the element type with const and volatile removed.
3The original C++20 standard did require that views have to have a default constructor, which is not the case for fixed spans. However, that requirement was removed later with http://wg21.link/P2325R3.
9.4 Span Operations |
|
|
|
|
|
|
|
313 |
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Operation |
|
Effect |
|
|
|
|
|
|
|
|
||
|
constructors |
|
Creates or copies a span |
|
|
|
|
|
|
||||
|
destructor |
|
Destroys a span |
|
|
|
|
|
|
|
|||
= |
|
|
Assigns a new sequence of values |
|
|
|
|
|
|||||
|
empty() |
|
Returns whether the span is empty |
|
|
|
|
|
|||||
|
size() |
|
Returns the number of elements |
|
|
|
|
|
|||||
|
size_bytes() |
|
Returns the size of memory used for all elements |
|
|
|
|||||||
[] |
|
|
Accesses an element |
|
|
|
|
|
|
|
|||
|
front(), back() |
|
Accesses the first or last element |
|
|
|
|
|
|||||
|
begin(), end() |
|
Provides iterator support (no const_iterator support) |
|
|
|
|||||||
|
rbegin(), rend() |
|
Provides constant reverse iterator support |
|
|
|
|
||||||
|
first(n) |
|
Returns a sub-span with dynamic extent of the first n elements |
||||||||||
|
first<n>() |
|
Returns a sub-span with fixed extent of the first n elements |
|
|
|
|||||||
|
last(n) |
|
Returns a sub-span with dynamic extent of the last n elements |
||||||||||
|
last<n>() |
|
Returns a sub-span with fixed extent of the last n elements |
|
|
|
|||||||
|
subspan(offs) |
|
Returns a sub-span with dynamic extent skipping the first offs elements |
||||||||||
|
subspan(offs, n) |
|
Returns a sub-span with dynamic extent of n after offs elements |
||||||||||
|
subspan<offs>() |
|
Returns a sub-span with same extent skipping the first offs elements |
||||||||||
|
subspan<offs, n>() |
|
Returns a sub-span with fixed extent of n after offs elements |
|
|
|
|||||||
|
data() |
|
Returns a raw pointer to the elements |
|
|
|
|
|
|||||
|
as_bytes() |
|
Returns the memory of the elements as a span of read-only |
||||||||||
|
|
|
|
|
std::bytes |
|
|
|
|
|
|
|
|
|
as_writable_bytes() |
|
Returns the memory of the elements as a span of writable std::bytes |
||||||||||
|
|
|
|
|
|
Table 9.1. Span operations |
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Member |
|
Effect |
|
|
|
|
|
|
|
|
|
|
|
extent |
|
Number of elements or std::dynamic_extent if size varies |
|
|
|
||||||
|
|
size_type |
|
Type of extent (always std::size_t) |
|
|
|
|
|
||||
|
|
difference_type |
|
Difference |
type |
for |
pointers |
to |
elements |
(always |
|||
|
|
|
|
std::difference_type) |
|
|
|
|
|
|
|||
|
|
element_type |
|
Specified type of the elements |
|
|
|
|
|
|
|||
|
|
pointer |
|
Type of a pointer to the elements |
|
|
|
|
|
||||
|
|
const_pointer |
|
Type of a pointer for read-only access to the elements |
|
|
|
||||||
|
|
reference |
|
Type of a reference to the elements |
|
|
|
|
|
||||
|
|
const_reference |
|
Type of a reference for read-only access to the elements |
|
|
|
||||||
|
|
iterator |
|
Type of an iterator to the elements |
|
|
|
|
|
||||
|
|
reverse_iterator |
|
Type of a reverse iterator to the elements |
|
|
|
|
|
||||
|
|
value_type |
|
Type of elements without const or volatile |
|
|
|
|
Table 9.2. Static and type members of spans
![](/html/75672/2303/html_Pld0VJ4wzF.QW__/htmlconvd-7poybx340x1.jpg)
314 Chapter 9: Spans
9.4.2 Constructors
For spans, the default constructor is provided only if it has a dynamic extent or the extent is 0:
std::span<int> sp0a; |
// OK |
std::span<int, 0> sp0b; |
// OK |
std::span<int, 5> sp0c; |
// compile-time ERROR |
If this initialization is valid, size() is 0 and data() is nullptr.
In principle, you can initialize a span for an array, a beginning with a sentinel (end iterator), and a beginning with a size. The latter is used by the view std::views::counted(beg, sz) if beg points to elements in contiguous memory. Class template argument deduction is also supported.
This means that when initializing the span with a raw array or std::array<>, a span with a fixed extent is deduced (unless only the element type is specified):
int a[10] {};
std::array arr{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
std::span sp1a{a}; |
// std::span<int, 10> |
std::span sp1b{arr}; |
// std::span<int, 15> |
std::span<int> sp1c{arr}; |
// std::span<int> |
std::span sp1d{arr.begin() + 5, 5}; |
// std::span<int> |
auto sp1e = std::views::counted(arr.data() + 5, 5); |
// std::span<int> |
When initializing the span with a std::vector<>, a span with a dynamic extent is deduced unless a size is specified explicitly:
std::vector vec{1, 2, 3, 4, 5}; |
|
std::span sp2a{vec}; |
// std::span<int> |
std::span<int> sp2b{vec}; |
// std::span<int> |
std::span<int, 2> sp2c{vec}; |
// std::span<int, 2> |
std::span<int, std::dynamic_extent> sp2d{vec}; |
// std::span<int> |
std::span<int, 2> sp2e{vec.data() + 2, 2}; |
// std::span<int, 2> |
std::span sp2f{vec.begin() + 2, 2}; |
// std::span<int> |
auto sp2g = std::views::counted(vec.data() + 2, 2); |
// std::span<int> |
If you initialize a span from an rvalue (temporary object), the elements have to be const:
std::span sp3a{getArrayOfInt()}; |
// ERROR: rvalue and not const |
std::span<int> sp3b{getArrayOfInt()}; |
// ERROR: rvalue and not const |
std::span<const int> sp3c{getArrayOfInt()}; |
// OK |
std::span sp3d{getArrayOfConstInt()}; |
// OK |
std::span sp3e{getVectorOfInt()}; std::span<int> sp3f{getVectorOfInt()}; std::span<const int> sp3g{getVectorOfInt()};
//ERROR: rvalue and not const
//ERROR: rvalue and not const
//OK
9.4 Span Operations |
315 |
Initializing a span with a returned temporary collection can result in a fatal runtime error. For example, you should never use a range-based for loop to iterate over a span that is initialized directly:
for (auto elem : std::span{getCollOfConst()}) ... // fatal runtime error
for (auto elem : std::span{getCollOfConst()}.last(2)) ... // fatal runtime error
for (auto elem : std::span<const Type>{getColl()}) ... // fatal runtime error
The problem is that the range-based for loop causes undefined behavior when iterating over a reference returned for a temporary object because the temporary object is destroyed before the loop starts to iterate internally. This is a bug that the C++ standards committee has not been willing to fix for years now (see http://wg21.link/p2012).
As a workaround, you can use the new range-based for loop with initialization: |
|
for (auto&& coll = getCollOfConst(); auto elem : std::span{coll}) ... |
// OK |
Regardless of whether you initialize a span with an iterator and a length or with two iterators that define a valid range, the iterators have to refer to elements in contiguous memory (satisfy the concept std::contiguous_iterator):
std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::span<int> sp6a{vec}; |
// OK, refers to all elements |
std::span<int> sp6b{vec.data(), vec.size()}; |
// OK, refers to all elements |
std::span<int> sp6c{vec.begin(), vec.end()}; |
// OK, refers to all elements |
std::span<int> sp6d{vec.data(), 5}; |
// OK, refers to first 5 elements |
std::span<int> sp6e{vec.begin()+2, 5}; |
// OK, refers to elements 3 to 7 (including) |
std::list<int> lst{ ... }; |
|
std::span<int> sp6f{lst.begin(), lst.end()}; |
// compile-time ERROR |
If the span has a fixed extent, it must match the number of elements in the passed range. In general, compilers cannot check this at compile time:
std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::span<int, 10> sp7a{vec}; std::span<int, 5> sp7b{vec}; std::span<int, 20> sp7c{vec}; std::span<int, 5> sp7d{vec, 5}; std::span<int, 5> sp7e{vec.begin(), 5}; std::span<int, 3> sp7f{vec.begin(), 5}; std::span<int, 8> sp7g{vec.begin(), 5}; std::span<int, 5> sp7h{vec.begin()};
//OK, refers to all elements
//runtime ERROR (undefined behavior)
//runtime ERROR (undefined behavior)
//compile-time ERROR
//OK, refers to first 5 elements
//runtime ERROR (undefined behavior)
//runtime ERROR (undefined behavior)
//compile-time ERROR
316 |
Chapter 9: Spans |
You can also create and initialize a span directly with a raw array or a std::array. In that case, some runtime errors due to an invalid number of elements become a compile-time error:
int raw[10];
std::array arr{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::span<int> sp8a{raw}; |
// OK, refers to all elements |
std::span<int> sp8b{arr}; |
// OK, refers to all elements |
std::span<int, 5> sp8c{raw}; |
// compile-time ERROR |
std::span<int, 5> sp8d{arr}; |
// compile-time ERROR |
std::span<int, 5> sp8e{arr.data(), 5}; |
// OK |
That is: either you pass a container with sequential elements as a whole, or you pass two arguments to specify the initial range of elements. In any case, the number of elements must match a specified fixed extent.
Construction with Implicit Conversions
Spans have to have the element type of the elements of the sequence they refer to. Conversions (even implicit standard conversions) are not supported. However, additional qualifiers such as const are allowed. This also applies to copy constructors:
std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::span<const int> sp9a{vec}; std::span<long> sp9b{vec}; std::span<int> sp9c{sp9a}; std::span<const long> sp9d{sp9a};
//OK: element type with const
//compile-time ERROR: invalid element type
//compile-time ERROR: removing constness
//compile-time ERROR: different element type
To allow containers to refer to the elements of user-defined containers, these containers have to signal that they or their iterators require that all elements are in contiguous memory. For that, they have to fulfill the concept contiguous_iterator.
The constructors also allow the following type conversions between spans:
•Spans with a fixed extent convert to spans with the same fixed extent and additional qualifiers.
•Spans with a fixed extent convert to spans with dynamic extent.
•Spans with a dynamic extent convert to spans with a fixed extent, provided the current extent fits.
Using a conditional explicit, only the constructors of spans with fixed extent are explicit. In that case, copy initialization (using a =) is not possible if the initial value has to be converted:
std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::span<int> spanDyn{vec.begin(), 5}; |
// OK |
std::span<int> spanDyn2 = {vec.begin(), 5}; |
// OK |
std::span<int, 5> spanFix{vec.begin(), 5}; |
// OK |
std::span<int, 5> spanFix2 = {vec.begin(), 5}; |
// ERROR |
9.4 Span Operations |
317 |
As a consequence, conversions are only implicitly supported when converting to a span with a dynamic or the same fixed extent:
void fooDyn(std::span<int>); void fooFix(std::span<int, 5>);
fooDyn({vec.begin(), 5}); |
// OK |
fooDyn(spanDyn); |
// OK |
fooDyn(spanFix); |
// OK |
fooFix({vec.begin(), 5}); |
// ERROR |
fooFix(spanDyn); |
// ERROR |
fooFix(spanFix); |
// OK |
spanDyn = spanDyn; |
// OK |
spanDyn = spanFix; |
// OK |
spanFix = spanFix; |
// OK |
spanFix = spanDyn; |
// ERROR |
9.4.3Operations for Sub-Spans
The member functions that create a sub-span can create spans with dynamic or fixed extent. Passing a size as a call parameter usually yields a span with dynamic extent. Passing a size as a template parameter usually yields a span with fixed extent. This is at least always the case for the member functions first() and last():
std::vector vec{1.1, 2.2, 3.3, 4.4, 5.5}; std::span spDyn{vec};
auto sp1 = spDyn.first(2); auto sp2 = spDyn.last(2); auto sp3 = spDyn.first<2>(); auto sp4 = spDyn.last<2>();
//first 2 elems with dynamic extent
//last 2 elems with dynamic extent
//first 2 elems with fixed extent
//last 2 elems with fixed extent
std::array arr{1.1, 2.2, 3.3, 4.4, 5.5}; std::span spFix{arr};
auto sp5 = spFix.first(2); auto sp6 = spFix.last(2); auto sp7 = spFix.first<2>(); auto sp8 = spFix.last<2>();
//first 2 elems with dynamic extent
//last 2 elems with dynamic extent
//first 2 elems with fixed extent
//last 2 elems with fixed extent
However, for subspan(), the result may sometimes be surprising. Passing call parameters always yields spans with dynamic extent:
std::vector vec{1.1, 2.2, 3.3, 4.4, 5.5}; std::span spDyn{vec};