Object Oriented Programming using 'C++

Chapter 11
Modern C++ Architecture and Language Features

Post Graduate Program in Computer Science Engineering
Object Oriented Programming using 'C++   •   Post Graduate

11. Modern C++ Architecture and Language Features

Introduction and Context

Modern C++ is more than an object-oriented language with additional syntax. It is a systems programming language that combines abstraction, performance, compile-time computation, generic programming, resource control, and increasingly expressive support for concurrency and asynchronous computation. Since C++11, successive language standards have transformed the way robust software is designed. Features introduced in C++17, C++20, and C++23 encourage developers to express intent more precisely while reducing boilerplate and common sources of error.

This chapter examines modern C++ from an architectural perspective. The focus is not merely on individual language features, but on how those features influence the structure of large software systems. Modules affect dependency management; structured bindings improve the use of compound results; `constexpr` moves computation toward compile time; concepts make generic interfaces more explicit; ranges support composable data processing; coroutines provide a foundation for asynchronous and generator-based designs; and functional techniques encourage transformation-oriented, testable code.

These features are most valuable when used as part of a coherent architecture. A technically modern program can still be poorly designed if it contains tangled dependencies, unclear ownership, uncontrolled concurrency, or abstractions that hide operational costs. The central concern is therefore architectural judgment: selecting language facilities that improve maintainability, correctness, performance, and organizational scalability.

Learning Objectives

After studying this chapter, the learner should be able to:

1. Explain the architectural significance of modern C++ language features.

2. Describe the purpose and practical status of modules and structured bindings.

3. Use `constexpr`, `consteval`, and `constinit` appropriately in compile-time design.

4. Apply concepts to constrain generic interfaces and improve diagnostics.

5. Construct range pipelines and evaluate their laziness and lifetime implications.

6. Explain coroutine mechanics and identify suitable asynchronous use cases.

7. Apply functional programming techniques within an object-oriented C++ architecture.

8. Design layered, component-based, and event-driven systems using modern C++.

9. Evaluate performance, ownership, safety, governance, and maintainability trade-offs.

10. Select appropriate modern features for professional software projects.

From Language Features to Architectural Decisions

A language feature becomes architecturally significant when it changes how components are separated, how contracts are expressed, or how execution and resources are managed. For example, a concept is not simply a replacement for a template declaration. It is an executable statement of the requirements imposed on a generic component. Similarly, a coroutine is not automatically an asynchronous solution; it is a mechanism that must be integrated with a scheduler, an awaitable type, and an error-handling policy.

A useful architectural model divides a C++ system into four related concerns:

  • Interface: What services, types, and guarantees does a component expose?
  • Implementation: How are those services realized and compiled?
  • Composition: How are components connected without creating excessive coupling?
  • Execution: How do operations proceed with respect to time, threads, events, and resources?

Modules primarily address interface and implementation boundaries. Concepts and structured bindings improve interface expression. `constexpr` influences implementation and configuration. Ranges and functional techniques improve composition. Coroutines affect execution models. None of these features removes the need for sound design principles such as cohesion, information hiding, dependency inversion, explicit ownership, and disciplined testing.

Modules: Architectural Boundaries for C++ Programs

Traditional C++ uses header files to share declarations. The preprocessor textually includes a header into every translation unit that requests it. This model is powerful but has well-known costs: repeated parsing, macro leakage, accidental dependence on transitive includes, and fragile interactions between declarations. A header may expose implementation details that were never intended to be public.

C++ modules introduce a language-level mechanism for publishing and importing declarations. A module interface unit may export selected names:

```cpp

export module geometry;

export struct Point {

double x;

double y;

};

export double distance(Point a, Point b);

```

Another source file can import the module:

```cpp

import geometry;

int main() {

Point p{0.0, 0.0};

Point q{3.0, 4.0};

return distance(p, q) == 5.0 ? 0 : 1;

}

```

The important distinction is semantic. An imported module is processed as a compiled interface rather than as uncontrolled textual substitution. Names not exported remain private to the module implementation. This improves encapsulation and can reduce build dependencies, although actual build-system and compiler support must be considered carefully.

A module may contain an interface partition, an implementation unit, or internal partitions. A common organization is to export stable domain abstractions while keeping algorithms, helper classes, and third-party details private. This encourages a public design that is smaller and more deliberate.

Modules do not eliminate all dependency problems. A module interface can still become excessively broad, and cyclic architectural dependencies remain undesirable. Migration is also complicated by legacy headers, macro-based configuration, compiler differences, and build tools. Consequently, a practical adoption strategy is incremental: begin with new, cohesive components; define clear exported interfaces; isolate legacy includes; and measure build and dependency improvements.

Structured Bindings and Explicit Data Decomposition

Structured bindings allow several names to be initialized from an object or tuple-like result:

```cpp

std::map<std::string, int> counts;

for (const auto& [word, count] : counts) {

if (count > 10) {

std::cout << word << '\n';

}

}

```

They are especially useful when APIs return `std::pair`, `std::tuple`, arrays, or user-defined types that provide an appropriate decomposition interface. They make the relationship between a result and its components visible at the point of use.

```cpp

auto [value, inserted] = cache.emplace(key, object);

if (!inserted) {

use_existing(value);

}

```

The declaration’s `auto`, `const`, and reference qualifiers matter. `auto [a, b]` generally creates bindings associated with a new object, while `auto& [a, b]` binds by reference to an existing object. In range loops, `const auto& [key, value]` avoids copying and prevents mutation. A careless choice can cause either unnecessary copying or unintended modification.

Structured bindings are therefore a small feature with architectural value: they make multi-value contracts readable. They should not, however, be used to conceal the meaning of a poorly designed return type. If a function returns six unrelated values, decomposing the result may improve syntax while leaving the underlying interface confusing. Named domain types are preferable when semantic meaning is important.

Compile-Time Programming with `constexpr`

Compile-time computation can improve performance, validate configuration, and express invariants. A `constexpr` function is permitted to execute during compilation when its arguments and body satisfy constant-expression rules, but it can generally also be called at run time.

```cpp

constexpr std::size_t buffer_size(std::size_t elements,

std::size_t bytes_per_element) {

return elements * bytes_per_element;

}

constexpr auto capacity = buffer_size(256, sizeof(double));

```

`consteval` defines an immediate function: calls must be evaluated at compile time.

```cpp

consteval int square(int value) {

return value * value;

}

```

`constinit` requires a variable with static or thread storage duration to be initialized statically. It is useful when initialization order and startup behavior matter, but it does not make the object immutable.

Compile-time techniques are valuable for fixed-size data structures, protocol constants, lookup tables, dimensional calculations, parsers for restricted formats, and policy selection. They can also enforce invalid configurations early:

```cpp

template<std::size_t N>

struct Packet {

static_assert(N > 0 && N <= 4096);

std::array<std::byte, N> data{};

};

```

Compile-time programming has costs. Complex template computations may increase build time and produce difficult diagnostics. Code that is technically evaluable at compile time may not be clearer when written as an elaborate metaprogram. The design principle is to move stable, deterministic, and configuration-oriented work to compilation while keeping business rules understandable and testable.

Concepts: Named Requirements for Generic Components

Before C++20, generic code often relied on substitution failure or long compiler diagnostics to indicate that a type did not meet an implicit requirement. Concepts allow those requirements to be named and checked at the interface boundary.

```cpp

template<class T>

concept Numeric = std::integral<T> || std::floating_point<T>;

template<Numeric T>

T average(T a, T b) {

return (a + b) / static_cast<T>(2);

}

```

A concept is a compile-time predicate. It can combine standard concepts, expressions, and logical conditions:

```cpp

template<class T>

concept HasSize = requires(const T& value) {

{ value.size() } -> std::convertible_to<std::size_t>;

};

```

Concepts improve three aspects of architecture. First, they make generic contracts visible. Second, they improve overload selection by expressing semantic categories. Third, they usually produce more localized diagnostics than unconstrained templates.

A concept should describe a meaningful requirement rather than merely reproduce implementation syntax. For example, a `SortableRange` concept might require a range, a sortable iterator, and an appropriate ordering relation. Overly narrow concepts reduce reuse; overly broad concepts permit operations that later fail in unintuitive ways. Concepts are most effective when they correspond to capabilities such as readable, movable, hashable, or ordered.

Concepts do not replace runtime validation. A type may satisfy a syntactic requirement while violating a domain invariant. Generic components therefore need both compile-time constraints and appropriate runtime checks.

Ranges and Composable Dataflow

The ranges library provides a uniform way to describe sequences and operations over them. It separates the source of data from transformations and terminal actions. A pipeline is commonly expressed with the pipe operator:

```cpp

namespace views = std::views;

auto result = values

| views::filter([](int x) { return x % 2 == 0; })

| views::transform([](int x) { return x * x; })

| views::take(5);

```

Many views are lazy. The filtering and transformation operations are not necessarily performed when the pipeline is declared; they occur as the result is traversed. Laziness permits composition and can avoid materializing intermediate collections. It also means that a view may refer to an underlying range whose lifetime must exceed the view’s use.

The distinction between a view and an owning container is architecturally important. A view is generally a lightweight description of traversal. It does not automatically copy or own the data. Returning a view over a local container creates a dangling reference. Similarly, storing a view for later use requires careful analysis of the source object’s lifetime.

Ranges support clearer data-processing components. A repository may expose a range of records, a validation layer may filter invalid records, and an application service may transform them into output models. However, a long pipeline can become difficult to debug. Named intermediate views, well-designed predicates, and terminal materialization at clear boundaries improve maintainability.

Ranges and concepts complement one another. A function can require `std::ranges::input_range` or a stronger range concept, then operate generically over a broad family of sources. This allows the same algorithm to work with containers, views, generated sequences, and adapters.

Functional Techniques in an Object-Oriented Language

Functional programming emphasizes expressions, transformation, immutability, higher-order functions, and minimizing hidden state. C++ remains multi-paradigm, so functional techniques should be integrated selectively rather than treated as a replacement for object-oriented design.

Standard algorithms and lambdas provide the basic vocabulary:

```cpp

std::transform(input.begin(), input.end(), output.begin(),

[](const Record& record) {

return normalize(record);

});

```

A lambda may capture by value or reference. Capturing by reference is efficient but creates a lifetime dependency; capturing by value makes a copy and may be safer for delayed execution. In asynchronous code, capture choices become especially significant because the lambda may execute after the surrounding scope has ended.

Pure functions, which depend only on their inputs and have no observable side effects, are easier to test and parallelize. Functional error handling can be represented through types such as `std::optional`, `std::expected` in C++23, or domain-specific result types. For example, parsing can return either a value or an error without using exceptions for ordinary invalid input.

Functional techniques do not imply that all state should be eliminated. Stateful objects are appropriate for resource managers, caches, devices, and transaction contexts. The architectural aim is to isolate stateful boundaries and make transformations as explicit as practical.

Coroutines and Cooperative Asynchronous Design

A coroutine is a function whose execution can be suspended and resumed. C++ coroutines are language-level support for this model, but they do not themselves provide a scheduler, an event loop, a thread pool, or an I/O system. Those elements are supplied by libraries and application architecture.

A generator-like coroutine may use `co_yield`:

```cpp

generator<int> numbers(int limit) {

for (int i = 0; i < limit; ++i)

co_yield i;

}

```

An asynchronous operation may use `co_await`:

```cpp

task<Response> fetch_data(Request request) {

auto connection = co_await connect_async(request.endpoint);

auto response = co_await connection.send(request);

co_return response;

}

```

The return type controls coroutine behavior. It typically contains a promise type, which defines how values, exceptions, suspension, and completion are handled. An awaitable object defines whether an operation is ready, how suspension is initiated, and how the result is obtained.

Coroutines can make asynchronous control flow resemble sequential code, reducing nested callbacks and clarifying error propagation. They are useful for network services, cooperative task systems, parsers, generators, and event-driven applications. Nevertheless, a coroutine may suspend while holding a lock, reference, iterator, or pointer that becomes invalid. It may also resume on a different thread, depending on the scheduler.

Important questions include: who owns the coroutine frame, what happens on cancellation, where exceptions are delivered, whether resumption is thread-safe, and how backpressure is represented? A production coroutine abstraction must answer these questions explicitly. Coroutines are therefore an architectural commitment, not merely a syntactic convenience.

Layered, Component-Based, and Event-Driven Architectures

Modern C++ features are most useful when placed within a clear system structure. A layered architecture may contain presentation, application, domain, and infrastructure layers. The domain layer should express business concepts without depending directly on user-interface or database technologies. Modules can expose domain interfaces while hiding implementation details. Concepts can constrain reusable infrastructure. Ranges can process data at application boundaries, and coroutines can coordinate asynchronous infrastructure operations.

A component-based architecture organizes the system around independently understandable units with explicit interfaces. A component should have a focused responsibility, stable contracts, controlled dependencies, and a clear ownership model. C++ supports this through modules, interfaces based on abstract classes or concepts, value types, dependency injection, and implementation hiding.

An event-driven architecture represents activity as events delivered to handlers or subscribers. Events may be synchronous or asynchronous. A simple event bus might use a type-safe subscription mechanism, but production designs must define ordering, delivery guarantees, error handling, reentrancy, and lifetime management. Coroutines can implement event consumers, while ranges can process event streams when the abstraction supports safe iteration.

A useful separation is:

1. Event production: detects or receives external activity.

2. Event transport: queues, routes, and possibly persists events.

3. Event handling: applies domain behavior.

4. Observation and recovery: records outcomes, retries failures, and supports diagnosis.

An event bus that stores raw pointers to subscribers creates serious lifetime risks. Safer designs use scoped subscriptions, stable ownership, or explicit cancellation tokens. Similarly, asynchronous handlers should avoid assuming that event order is preserved unless the transport guarantees it.

An Illustrative Case Study: Modernizing a Sensor Gateway

Background

Consider an illustrative industrial sensor gateway that receives measurements from multiple devices, validates them, aggregates selected readings, and forwards alerts to a cloud service. The original implementation consists of a large executable, global configuration variables, manually managed threads, callback chains, and a collection of shared headers.

Situation and Problem

The system suffers from long build times, inconsistent validation, occasional use-after-free errors in callbacks, and difficult testing. Every protocol adapter includes the same configuration headers. A new protocol requires changes in unrelated parts of the program. The operations team also wants graceful shutdown and bounded buffering when the cloud service is unavailable.

Analysis

The problems are architectural rather than purely syntactic. Build dependencies are not controlled; ownership of asynchronous operations is unclear; data processing is intertwined with transport; and the system has no explicit backpressure policy. Introducing isolated modern features would not solve these problems. The modernization must establish boundaries first.

Approach and Solution

The team divides the gateway into modules: `configuration`, `protocol`, `measurement`, `storage`, and `transport`. Only stable domain types and service interfaces are exported from module interfaces. Legacy protocol headers are confined to implementation units.

Measurements are represented by value-oriented types. A protocol adapter returns a result containing either a measurement or a structured error. Structured bindings make status handling readable, while concepts constrain generic validation algorithms to readable ranges of measurements.

A range pipeline performs validation and normalization:

```cpp

auto usable = readings

| std::views::filter(is_valid)

| std::views::transform(normalize)

| std::views::take(max_batch);

```

The pipeline is materialized into a bounded batch before transmission. This explicit boundary prevents a view from outliving its source and gives the transport layer a concrete unit for retry and accounting.

Compile-time configuration is used for protocol limits and buffer capacities. `static_assert` checks ensure that deployment parameters are internally consistent. Runtime configuration remains validated at startup because it depends on external files and environment values.

The transport layer exposes an asynchronous task interface implemented with coroutines. A task awaits connection, transmission, and acknowledgement. Cancellation is represented explicitly, and the coroutine owns its operation state through a managed task object. The event loop controls resumption; the coroutine itself does not assume that it runs on a particular thread.

Outcome

The resulting architecture reduces accidental dependencies and separates measurement rules from transport details. Tests can exercise normalization with ordinary containers, while integration tests use simulated protocol and network components. Bounded batches and explicit cancellation prevent unlimited memory growth during network outages. The design does not depend on every feature being used everywhere: modules are applied at boundaries, ranges in dataflow, concepts in generic interfaces, and coroutines only in genuinely asynchronous paths.

Learning Points

The case demonstrates five principles. First, architecture should precede feature adoption. Second, views require lifetime discipline. Third, asynchronous abstractions must define ownership and cancellation. Fourth, compile-time validation complements but does not replace runtime validation. Finally, modern C++ is most effective when each feature has a clear responsibility within the design.

Challenges, Risks, and Common Mistakes

Modern C++ features introduce expressive power but also new failure modes. A range pipeline may contain a dangling view. A lambda may capture a reference used after its scope ends. A coroutine may retain a lock across suspension or resume after its dependent object has been destroyed. A concept may encode syntactic capability without guaranteeing semantic correctness.

Build portability is another concern. Compiler, standard-library, and build-system support for modules and newer library facilities may differ. Teams should define a supported toolchain and test it continuously. A project should not claim to use a language standard merely because a compiler accepts some of its features.

Overuse is equally harmful. Replacing every loop with a pipeline can obscure control flow. Introducing coroutines for simple synchronous operations adds conceptual cost. Excessive template and compile-time computation can lengthen builds. Concepts named after implementation details may expose unstable design decisions.

Performance assumptions must be measured. Lazy ranges can reduce allocations, but repeated traversal or expensive predicates may produce unexpected costs. Compile-time computation can reduce runtime work while increasing build time. Coroutines may improve code organization without automatically improving throughput. Profiling and workload-specific testing remain essential.

Professional, Ethical, and Governance Considerations

Software architecture affects safety, reliability, and accountability. In industrial, medical, financial, or public-sector systems, asynchronous failure and data loss may have consequences beyond program termination. Requirements for auditability, deterministic behavior, data retention, and recovery should be documented before selecting an event-driven design.

Developers should define ownership and cancellation policies in code reviews and architectural documentation. External input must be validated before entering generic algorithms or compile-time-like configuration paths. Sensitive data should not be accidentally copied through structured bindings, logging lambdas, or queued events. Dependency and toolchain provenance should be tracked, especially when modules expose third-party types.

Concurrency also creates governance concerns: who can observe an event, who can alter a handler, and how are failures reconstructed from logs? Type safety reduces certain errors but does not replace access control, secure deployment, threat modeling, or operational monitoring.

Current Developments and Emerging Directions

C++ continues to evolve toward safer and more composable abstractions. C++23 expands standard library support for ranges and introduces facilities such as `std::expected`, which provides a value-or-error representation for many operations. Work on senders and receivers seeks a more uniform model for composing asynchronous operations. Standardization efforts also continue around improved executors, networking, reflection, and library ergonomics.

The practical direction is clear even when individual proposals change: systems are moving toward explicit constraints, better value-based error handling, composable dataflow, structured asynchronous operations, and stronger separation between interface and implementation. Organizations should adopt features according to compiler maturity, team competence, operational requirements, and migration value rather than language-version enthusiasm.

Best Practices for Modern C++ Architecture

  • Define ownership, lifetime, and cancellation before implementing asynchronous components.
  • Keep module interfaces small, stable, and free from unnecessary implementation dependencies.
  • Use concepts to express meaningful capabilities, not merely to shorten syntax.
  • Treat views as non-owning unless their ownership semantics are explicitly documented.
  • Prefer named domain types when decomposed values have substantial semantic meaning.
  • Use `constexpr` for stable, deterministic computation and validate external configuration at runtime.
  • Isolate side effects and keep transformations as pure as practical.
  • Establish materialization boundaries in long-lived or asynchronous range pipelines.
  • Measure build time, memory use, latency, and throughput after modernization.
  • Document the event model: ordering, delivery, retry, failure, and shutdown behavior.
  • Introduce features incrementally and maintain a tested, supported toolchain.

Chapter Summary

Modern C++ language features influence architecture by improving boundaries, contracts, composition, and execution models. Modules provide language-level interface separation and can reduce dependency pollution. Structured bindings clarify the use of compound results, provided that the underlying return types are meaningful. `constexpr`, `consteval`, and `constinit` support compile-time computation and initialization control, while concepts express generic requirements explicitly.

Ranges enable lazy, composable dataflow, but their non-owning nature makes lifetime analysis essential. Functional techniques improve transformation, testing, and error handling without eliminating the value of stateful objects. Coroutines simplify the expression of generators and asynchronous workflows, but require explicit policies for scheduling, ownership, cancellation, exceptions, and backpressure.

The architectural lesson is that modern features should be assigned clear roles within layered, component-based, or event-driven systems. Successful modernization combines language facilities with disciplined dependency management, measurement, testing, security, and governance.

Key Terms

Module: A language-level unit that exports selected declarations and hides non-exported implementation details.

Module interface unit: A source unit that defines the declarations exported by a module.

Structured binding: A declaration that introduces names corresponding to elements of an aggregate or tuple-like object.

Constant expression: An expression that can be evaluated during compilation under language rules.

`constexpr`: A specifier permitting a function or object to participate in constant evaluation.

`consteval`: A specifier requiring a function call to be evaluated at compile time.

`constinit`: A specifier requiring static initialization of a variable with static or thread storage duration.

Concept: A compile-time predicate describing requirements on template arguments.

Range: An abstraction representing a sequence that can be traversed through an iterator interface.

View: A usually non-owning, often lazy range adaptor or sequence representation.

Lazy evaluation: Delaying computation until a result is actually requested.

Functional technique: A programming practice emphasizing expressions, transformations, higher-order functions, and controlled state.

Coroutine: A function whose execution can suspend and resume while preserving its state.

Awaitable: An object that defines the protocol used by `co_await`.

Coroutine frame: Storage containing the state required for a suspended coroutine.

Backpressure: A mechanism that prevents producers from overwhelming consumers or buffers.

Component architecture: A design organized around independently understandable units with explicit interfaces.

Event-driven architecture: An architecture in which components communicate through events and handlers.

Materialization: Converting a lazy or abstract sequence into an owning concrete container.

Dependency inversion: A design principle in which high-level policy depends on abstractions rather than low-level implementation details.

Review Questions

Short Answer Questions

1. How does a C++ module differ architecturally from a traditional header file?

2. What is the difference between `constexpr` and `consteval`?

3. Why must the lifetime of an underlying range be considered when using views?

4. What role does a concept play in a generic interface?

5. Why does a coroutine require supporting infrastructure such as a scheduler or event loop?

Long Answer Questions

1. Explain how modules, concepts, and ranges can work together in a component-based C++ architecture.

2. Discuss the benefits and risks of compile-time programming using `constexpr`, `consteval`, and `constinit`.

3. Describe the design responsibilities involved in building a production-quality coroutine abstraction.

4. Compare layered, component-based, and event-driven architectures in the context of modern C++.

5. Explain how functional programming techniques can improve an object-oriented C++ system without eliminating stateful components.

Analytical / Application Questions

1. A range pipeline is returned from a function, but it refers to a local `std::vector`. Analyze the defect and propose two safe redesigns.

2. Design a modernization plan for a callback-based network service that has unclear ownership, uncontrolled buffering, and excessive header dependencies. Identify which modern C++ features you would introduce and why.

3. A team proposes replacing every template constraint with one broad concept named `ValidType`. Evaluate this proposal and recommend a more effective approach.

22. Multiple Choice Questions with Answers

Question 1

Which architectural problem is most directly addressed by C++ modules?

A. Eliminating all runtime memory allocation

B. Controlling interface visibility and reducing textual dependency pollution

C. Automatically parallelizing every algorithm

D. Replacing all object-oriented abstractions

Correct Answer: B. Controlling interface visibility and reducing textual dependency pollution

Question 2

In the declaration `const auto& [key, value]`, what is the principal effect of the reference and const qualifiers?

A. They create mutable copies of both elements

B. They bind to the elements without permitting modification through the bindings

C. They force both elements to be evaluated at compile time

D. They convert the elements into pointers

Correct Answer: B. They bind to the elements without permitting modification through the bindings

Question 3

Which statement correctly distinguishes `consteval` from `constexpr`?

A. `consteval` functions must be evaluated at compile time, whereas `constexpr` functions may also run at runtime

B. `constexpr` functions must always run at runtime, whereas `consteval` functions are optional

C. They are identical except for spelling

D. `consteval` applies only to variables

Correct Answer: A. `consteval` functions must be evaluated at compile time, whereas `constexpr` functions may also run at runtime

Question 4

What is the primary purpose of a C++ concept?

A. To allocate objects on the stack

B. To describe compile-time requirements on template arguments

C. To schedule coroutine resumption

D. To replace all runtime validation

Correct Answer: B. To describe compile-time requirements on template arguments

Question 5

Why can returning a view over a local container be unsafe?

A. Views always copy the container twice

B. The view may refer to an object whose lifetime has ended

C. Views cannot be used with standard containers

D. Lazy evaluation prevents iteration

Correct Answer: B. The view may refer to an object whose lifetime has ended

Question 6

Which statement about C++ coroutines is accurate?

A. They automatically create threads and an event loop

B. They can suspend and resume, but scheduling and ownership require supporting design

C. They cannot propagate exceptions

D. They are limited to mathematical generators

Correct Answer: B. They can suspend and resume, but scheduling and ownership require supporting design

Question 7

A generic sorting function should accept only types that provide an appropriate ordering operation. Which modern feature most directly expresses this requirement?

A. A preprocessor macro

B. A concept

C. A structured binding

D. A module partition alone

Correct Answer: B. A concept

Question 8

In the sensor gateway case, why was a range pipeline materialized into a bounded batch before transmission?

A. To guarantee that all processing occurs at compile time

B. To establish a concrete ownership and buffering boundary for asynchronous transport

C. To eliminate the need for validation

D. To make the module interface private

Correct Answer: B. To establish a concrete ownership and buffering boundary for asynchronous transport

Question 9

Which is a significant risk when a lambda used asynchronously captures a local variable by reference?

A. The lambda becomes a module interface

B. The referenced object may no longer exist when the lambda executes

C. The lambda cannot perform transformations

D. The compiler must always reject the program

Correct Answer: B. The referenced object may no longer exist when the lambda executes

Question 10

What is the most appropriate architectural principle for adopting modern C++ features?

A. Use every new feature throughout the codebase immediately

B. Select features according to clear responsibilities, ownership rules, toolchain support, and measured value

C. Avoid all library abstractions in performance-sensitive systems

D. Replace runtime tests with compile-time checks wherever possible

Correct Answer: B. Select features according to clear responsibilities, ownership rules, toolchain support, and measured value

Object Oriented Programming using 'C++
Chapter 11 of 15