Understanding Move Semantics: Efficiency in Modern C++

Posts

Move semantics is a fundamental concept introduced in C++11 that revolutionizes the way resource management and object copying are handled in C++. Before move semantics, copying objects, especially those managing resources like dynamically allocated memory, file handles, or network sockets, involved deep copies. Deep copying means duplicating every resource the object owns, which can be extremely expensive in terms of time and memory. This copying is often unnecessary, particularly when dealing with temporary objects that will soon be destroyed.

Move semantics allow the transfer of resources from one object to another without performing a deep copy. Instead of copying, it moves ownership of resources, leaving the original object in a valid but unspecified state, often an empty or null state. This approach significantly improves performance, especially for large or complex objects such as strings, vectors, and other containers.

To understand move semantics, it is essential to grasp the concept of resource ownership. Many C++ objects manage resources behind the scenes, such as pointers to dynamically allocated memory. When copying such objects, the conventional approach is to duplicate the resource to ensure both the original and the copied object can manage their separate resources safely. While safe, this approach can be very inefficient.

Move semantics provides a way to avoid this duplication by transferring resource ownership. After a move, the source object relinquishes ownership, and the destination object assumes it, preventing double deletion and unnecessary allocations. This technique is especially beneficial when dealing with temporary objects, returned values from functions, or objects explicitly marked to be moved.

In summary, move semantics enable C++ programs to be more efficient by minimizing expensive deep copies and instead transferring resources wherever possible. This results in faster, less resource-intensive code, which is crucial in performance-critical applications.

How Move Semantics Work in C++

The essence of move semantics is the ability to transfer resources from one object to another without copying. This transfer involves “stealing” the resource handle, such as a pointer, from the source object and assigning it to the target object. The source object is then left in a safe, but typically empty or null, state.

Two primary features make this possible: rvalue references and move constructors or move assignment operators.

Rvalue references are a new type of reference introduced in C++11, denoted by double ampersands (&&). Unlike traditional references that bind to lvalues (objects with identifiable locations in memory), rvalue references bind to rvalues. Rvalues are temporary objects or values that are about to expire, such as the result of an expression or a temporary value returned from a function.

By binding to rvalues, rvalue references allow functions and constructors to distinguish between objects that can be moved and those that cannot. This distinction enables the compiler to optimize operations by invoking move constructors or move assignment operators when appropriate.

When a move constructor or move assignment operator is invoked, it takes ownership of the resource managed by the source object, avoiding expensive copying operations. The move constructor initializes a new object by transferring resources from a temporary object, while the move assignment operator transfers resources to an existing object, typically after freeing its previously owned resources.

The source object, having transferred its resources, is left in an empty or null state to ensure that it does not attempt to manage the resource again, preventing issues such as double deletion. The destination object now exclusively owns the resource.

This mechanism is transparent to the user once move constructors and move assignment operators are defined for a class. The compiler will automatically use them when it detects an rvalue or when std::move is explicitly used to cast an lvalue into an rvalue reference.

The efficiency gained through move semantics is substantial. Operations that would normally require costly deep copies now simply transfer ownership. This is particularly advantageous for large containers like vectors or strings, which can hold significant amounts of data.

Move Constructor and Move Assignment Operator

The move constructor and move assignment operator are the two critical member functions that enable move semantics in a class. They define how resources are transferred from one object to another.

The move constructor is a special constructor that takes an rvalue reference to an object of the same class. It is called when a new object is being created from a temporary object or an object that can be safely moved. The move constructor transfers the resource pointer from the source object to the new object, then nullifies the pointer in the source object to leave it in a safe, empty state.

This transfer avoids duplicating the resource. Instead of allocating new memory and copying data, the new object simply assumes ownership of the existing resource, significantly improving performance.

Similarly, the move assignment operator is called when an existing object is assigned the value of an rvalue object. Before taking ownership of the new resource, the move assignment operator releases any resource the target object currently owns to avoid leaks. It then transfers the resource ownership from the source object, which is again left empty.

Both the move constructor and move assignment operator must carefully manage the state of the source object after the move to ensure the program remains safe and stable. The source should remain destructible, even though it no longer owns the resource.

Implementing these functions allows objects to be efficiently transferred rather than copied, especially during return statements, function calls, or container operations. Without them, the compiler falls back on copy semantics, which can be inefficient for resource-heavy objects.

Lvalues, Rvalues, and Rvalue References

A thorough understanding of move semantics requires familiarity with lvalues, rvalues, and rvalue references.

An lvalue refers to an object with a persistent location in memory. Variables and objects with names are lvalues because they can be referred to repeatedly throughout a program. Lvalues can appear on both sides of an assignment statement and generally cannot be moved safely because their lifetime extends beyond the current expression.

An rvalue is a temporary object or value that does not have a persistent address in memory. Examples include literals, temporary objects returned from functions, or the result of expressions. Rvalues usually appear on the right-hand side of an assignment and are candidates for moving because they are about to expire.

Rvalue references, denoted by &&, are a new type of reference introduced to bind specifically to rvalues. They allow programmers and the compiler to recognize objects that can be safely moved. By accepting rvalue references, move constructors and move assignment operators signal that the object can transfer ownership of its resources.

Because rvalue references can only bind to temporaries or objects explicitly cast to rvalues, the compiler can use them to distinguish between copy and move operations. This distinction enables the automatic use of move semantics when appropriate.

Functions such as std::move facilitate this process by casting an lvalue to an rvalue reference, telling the compiler that the object can be moved even though it has a name. This explicit move signals that the resource should be transferred, avoiding an expensive copy.

In summary, lvalues, rvalues, and rvalue references provide the language mechanisms that enable move semantics to improve the performance of modern C++ programs.

Using std::move to Enable Move Semantics

At the heart of move semantics lies the utility function known as std::move. Despite its name, std::move does not move anything itself. Instead, it is a cast that converts an lvalue into an rvalue reference, enabling move semantics to be applied. This cast is crucial because move constructors and move assignment operators only accept rvalue references as parameters.

When you have an object that is still named (an lvalue) but want to allow its resources to be transferred rather than copied, you use std::move to explicitly indicate that the object’s resources can be moved. This tells the compiler to treat the object as a temporary, making it eligible for move operations.

For example, consider a large object with dynamically allocated memory. Without std::move, passing or assigning this object would invoke the copy constructor or copy assignment operator, resulting in expensive copying of resources. With std::move, the move constructor or move assignment operator is called instead, transferring ownership of resources and avoiding unnecessary allocations.

The explicit use of std::move is necessary because not all lvalues can or should be moved from. Moving from an object implies that the object may lose its original data and be left in an empty state. Thus, the programmer must ensure that moving from the object is safe and appropriate. Using std::move makes this intent clear.

In many cases, compilers automatically apply move semantics when returning temporary objects from functions or when binding to rvalue references, so explicit use of std::move is not always required. However, when dealing with named objects that need to be moved, std::move is essential.

By leveraging std::move, C++ enables developers to optimize programs by eliminating unnecessary copying, reducing memory usage, and improving runtime performance, especially in scenarios involving large or complex objects.

When Move Semantics Apply

Move semantics are most beneficial in specific situations where the cost of copying resources would be high. Understanding when move semantics apply helps programmers write more efficient code.

One common scenario is when a function returns a large object by value. Traditionally, returning an object by value would trigger a copy constructor, duplicating the resources. With move semantics, the returned temporary object can be moved to the caller’s context, transferring ownership without copying. This optimization significantly reduces the overhead associated with returning large objects.

Another case is when temporary objects are passed as arguments to functions. If a function takes an argument by value or by rvalue reference, move semantics allow the resources of the temporary argument to be transferred instead of copied. This is particularly useful for objects like vectors, strings, or smart pointers.

Explicit use of std::move is also common when the programmer wants to transfer ownership from one named object to another. For example, when storing an object in a container or assigning it to another variable, calling std::move indicates that the source object’s resources should be moved rather than copied.

Move semantics also improve the performance of containers in the standard library. When elements are inserted, erased, or rearranged, containers can move elements instead of copying them, drastically reducing the overhead of these operations.

Lastly, move semantics prevent costly deep copies in user-defined classes that manage resources. By implementing move constructors and move assignment operators, these classes enable safe and efficient transfer of ownership, improving the overall performance of applications.

In summary, move semantics apply whenever resources can be transferred safely from one object to another without copying, particularly with temporary objects, function returns, argument passing, container operations, and explicit moves of named objects.

Optimizing Large Data Structures with Move Semantics

Large data structures, such as strings, vectors, and other standard containers, benefit tremendously from move semantics. These objects often manage dynamic memory or other resources that are expensive to copy.

Before move semantics, copying such objects involved deep copying of all the internal data. For example, copying a vector meant allocating new memory and duplicating all elements, which could be costly in both time and memory. Similarly, copying strings involved duplicating the entire character buffer.

With move semantics, these large objects can transfer ownership of their internal resources by simply moving pointers or handles. The move constructor or move assignment operator takes over the internal pointers of the source object and nullifies the source’s pointers. This means no new memory allocations or element-by-element copying is necessary.

This transfer is especially useful when objects are returned from functions or inserted into containers. For instance, when returning a large vector from a function, the move constructor allows the caller to take ownership of the vector’s data without copying.

Containers in the standard library are designed to leverage move semantics internally. Operations such as resizing, insertion, and swapping use move operations instead of copies whenever possible, making these operations more efficient.

Programmers can also optimize their data structures by implementing move constructors and move assignment operators. By doing so, they enable their classes to participate in move semantics, resulting in faster execution and reduced resource consumption.

In short, move semantics enable efficient management of large data structures by avoiding expensive copying and instead transferring ownership of resources, leading to significant performance gains.

Preventing Costly Copies and Enhancing Performance

One of the primary motivations for introducing move semantics into C++ was to prevent costly copies that degrade program performance. Without move semantics, objects are copied unnecessarily in many common scenarios, leading to wasted CPU cycles and excessive memory usage.

Copying involves allocating new memory, duplicating data, and performing complex copy operations. For large objects or objects managing scarce system resources, these operations can be very expensive and slow. Move semantics mitigate this cost by transferring resource ownership without duplication.

For example, consider a class managing a large buffer. Copying this class would require allocating a new buffer and copying all the data. Moving the class, however, only involves copying the pointer and setting the source pointer to null. This simple pointer swap is orders of magnitude faster than copying the entire buffer.

Similarly, containers such as vectors or maps rely heavily on copying when elements are inserted or moved. Move semantics allow these containers to optimize these operations by transferring ownership of the elements, significantly improving performance in real-world applications.

Move semantics also improve the efficiency of standard library algorithms and user-defined functions by eliminating unnecessary copies. When objects are passed by value, returned by value, or assigned, move semantics reduce overhead and speed up execution.

By preventing costly copies, move semantics contribute to writing faster, more resource-efficient code. This improvement is especially important in performance-critical domains such as gaming, real-time systems, and high-frequency trading, where every millisecond counts.

Move Semantics and Function Return Optimization

One of the most common and impactful uses of move semantics is in optimizing the return of objects from functions. Before move semantics, returning large objects by value could be expensive due to deep copying. Even with copy elision techniques, some copies were unavoidable in complex scenarios.

With move semantics, when a function returns a temporary object or a local object that is about to go out of scope, the compiler can invoke the move constructor instead of the copy constructor. This transfer of ownership of the internal resources from the local object to the caller’s object avoids the cost of duplicating data.

For example, consider a function that constructs and returns a large string or vector. When the function finishes, the temporary object it created is passed back to the caller. Instead of copying the entire data, the move constructor takes over the resources, leaving the temporary object in an empty state. This optimization can drastically reduce the overhead of function calls involving large objects.

Even though modern compilers perform Return Value Optimization (RVO) and Named Return Value Optimization (NRVO) to eliminate unnecessary copies, move semantics provide an additional mechanism that ensures efficient transfers when such optimizations are not applicable or are disabled.

Move semantics also facilitate returning objects by value in user-defined types where resource ownership is critical. Programmers can safely design functions to return objects without worrying about excessive copies, knowing that move constructors will handle efficient resource transfers.

Thus, move semantics help functions return large or complex objects efficiently, contributing to better application performance and clearer, safer code.

Passing Arguments and Move Semantics

Another key application of move semantics is in function argument passing. Traditionally, passing objects by value involves copying the argument, which can be costly for large or resource-heavy objects. Passing by reference avoids copying, but sometimes does not fit the semantics needed.

Move semantics offer a flexible alternative. By defining move constructors and move assignment operators, functions can accept rvalue references as parameters, allowing resources to be transferred rather than copied when temporary objects are passed.

This approach is particularly effective for functions that take ownership of resources or need to modify passed-in objects. If an argument is an rvalue, the function can move it directly, avoiding copying overhead.

Using perfect forwarding in template functions also complements move semantics. Perfect forwarding preserves the value category of arguments, forwarding lvalues as lvalues and rvalues as rvalues. This allows functions to efficiently accept and forward arguments without unnecessary copies or moves.

Programmers often combine move semantics with std::move in function calls to explicitly indicate that an argument can be moved. This practice improves performance and clarifies intent.

In summary, move semantics enable efficient argument passing by avoiding unnecessary copies and supporting safe transfer of ownership, which enhances function flexibility and performance.

Implementing Move Semantics in User-Defined Classes

Implementing move semantics in user-defined classes is a crucial step toward writing efficient C++ programs, especially when your classes manage resources such as dynamic memory, file handles, or network connections. Without move semantics, objects of these classes are copied unnecessarily, leading to performance bottlenecks and increased memory usage. In this section, we will explore the principles, patterns, and best practices for implementing move constructors and move assignment operators in your classes.

Why Implement Move Semantics in User-Defined Classes?

C++11 introduced move semantics to optimize the transfer of resources when an object is temporary or no longer needed in its original location. Standard library classes like std::vector, std::string, and smart pointers have built-in support for move semantics, enabling efficient operations without costly copying. However, for user-defined types that manage resources manually, the compiler-generated copy operations are often insufficient or inefficient.

By explicitly implementing move constructors and move assignment operators, you enable your classes to:

  • Transfer ownership of internal resources efficiently instead of duplicating them.
  • Avoid unnecessary deep copies of dynamically allocated memory or other handles.
  • Work seamlessly with standard library containers and algorithms that rely on move semantics.
  • Provide consistent behavior with modern C++ practices, improving performance and resource utilization.

The Rule of Five

Implementing move semantics ties closely to the “Rule of Five.” This rule states that if your class defines any of the following five special member functions, it should probably explicitly define all five to ensure proper resource management:

  1. Destructor
  2. Copy constructor
  3. Copy assignment operator
  4. Move constructor
  5. Move assignment operator

The reasoning behind this is to maintain consistent and safe behavior when objects are copied or moved, or when they are destroyed. For example, if you manually manage dynamic memory, your destructor needs to free it, and your copy operations need to perform deep copies. Likewise, your move operations should transfer ownership without duplicating resources.

Defining a Move Constructor

The move constructor is responsible for “stealing” the resources from a source object that is about to be destroyed or reused. It takes an rvalue reference to the class type and transfers the internal data to the newly constructed object. After the transfer, the source object is left in a valid but unspecified state, often by resetting pointers to null or zero.

Here is the general pattern for a move constructor:

cpp

CopyEdit

ClassName(ClassName&& other) noexcept

{

    // Transfer ownership of resources from other objects to this object

    , this->resourcePointer = other.resourcePointer;

    // Nullify or reset others’ resource pointer to prevent double deletion

    of others.resourcePointer = nullptr;

}

The noexcept specifier is important. It signals to the compiler and standard library that this move constructor will not throw exceptions, allowing optimizations such as in standard containers.

Implementing the Move Assignment Operator

The move assignment operator transfers resources from an existing rvalue object to an existing lvalue object. It must carefully handle several cases, including self-assignment, releasing existing resources, and ensuring exception safety.

A typical pattern for the move assignment operator looks like this:

cpp

CopyEdit

ClassName& operator=(ClassName&& other) noexcept

{

    if (this != &other)  // Protect against self-assignment

    {

        // Release current resources

        delete[] this->resourcePointer;

        // Transfer ownership from other

        this->resourcePointer = other.resourcePointer;

        // Reset others’ pointer

        to other.resourcePointer = nullptr;

    }

    return *this;

}

Again, marking the operator as noexcept allows better optimization. This operator must release any resources currently held by the object before taking ownership of new ones to prevent resource leaks.

The Move-and-Swap Idiom

To simplify the move assignment operator and ensure exception safety, many developers use the move-and-swap idiom. This technique combines move semantics with a swap function to manage resources safely and cleanly.

The approach involves:

  1. Creating a temporary object by moving the right-hand side operand.
  2. Swapping the contents of the temporary object with the current object.
  3. Letting the temporary object’s destructor release old resources when it goes out of scope.

Here is how it looks conceptually:

cpp

CopyEdit

void swap(ClassName& first, ClassName& second) noexcept

{

    std::swap(first.resourcePointer, second.resourcePointer);

}

ClassName& operator=(ClassName&& other) noexcept

{

    swap(*this, other);

    return *this;

}

This method automatically handles self-assignment safely, provides strong exception safety guarantees, and reduces code duplication. The swap function only exchanges pointers or small pieces of data, so it is efficient.

Managing Resource Ownership and Safety

When implementing move semantics, it is critical to ensure that resource ownership is transferred and that no double deletion or resource leaks occur. This means:

  • The moved-from object must be left in a valid state, typically with null or default values.
  • The destructor must correctly free resources only if the pointer is not null.
  • Copy operations must perform deep copies to maintain object independence.
  • Move operations must nullify source pointers to prevent shared ownership.

A consistent pattern of setting pointers to nullptr after moving resources helps ensure safety and prevents dangling pointers.

Exception Safety Considerations

Although move constructors and move assignment operators usually perform simple pointer transfers and are unlikely to throw exceptions, if your class manages resources that can throw during move (such as containers or file streams), you must ensure strong exception safety guarantees.

This often means:

  • Using the move-and-swap idiom to provide a strong exception guarantee.
  • Ensuring all operations in the move constructor and assignment are noexcept or wrapped in exception handling.
  • Avoid partial resource transfers that could leave objects in inconsistent states.

Exception safety is critical to writing robust, maintainable classes that behave correctly even in the presence of errors.

Example: Implementing Move Semantics in a Simple String Class

Consider a simplified string class that manages a dynamically allocated character buffer. Without move semantics, copying such a class requires allocating new memory and copying characters, which is expensive.

Implementing move semantics allows efficient resource transfer.

In the move constructor, the buffer pointer is taken from the source, and the source pointer is reset. In the move assignment operator, old buffers are released, then the source buffer is taken, and the source is reset.

This results in efficient transfers of string data without unnecessary copying.

Interaction with Copy Semantics

When you implement move semantics, remember to provide correct copy semantics as well. The copy constructor and copy assignment operator must perform deep copies to avoid shared ownership of resources.

If the copy operations are not explicitly defined, the compiler generates default shallow copies, leading to double deletions or resource leaks.

By implementing both copy and move operations, your class can handle all forms of assignment and construction efficiently and safely.

Testing and Debugging Move Semantics

Implementing move semantics can introduce subtle bugs if resource management is not handled carefully. Testing and debugging are essential.

Some strategies include:

  • Writing unit tests that move objects and verify the source is left in a valid state.
  • Using tools such as sanitizers to detect use-after-move or double deletion.
  • Adding debug output in constructors, destructors, and move operations to trace resource transfers.
  • Ensuring move operations are called by using std::move explicitly in tests.

Testing helps confirm that your move semantics implementations behave correctly and efficiently.

Implementing move semantics in user-defined classes is an essential skill in modern C++ programming. By carefully defining move constructors and move assignment operators, you enable your classes to transfer ownership of resources efficiently, avoid expensive copies, and integrate seamlessly with the C++ standard library.

Key points to remember include:

  • Follow the Rule of Five to manage all special member functions consistently.
  • Define move constructors and move assignment operators using rvalue references.
  • Use noexcept whenever possible to enable compiler optimizations.
  • Consider the move-and-swap idiom for safe and exception-resilient move assignment.
  • Nullify resource pointers in the source object to avoid double deletion.
  • Implement deep copy operations alongside move operations.
  • Thoroughly test move semantics to catch potential resource management errors.

Mastering move semantics in your classes results in faster, safer, and more efficient C++ programs.

The State of Objects After a Move

A subtle but important aspect of move semantics is the state of the source object after its resources have been moved. According to the C++ standard, the moved-from object must remain in a valid, destructible state, but is otherwise left in an unspecified state.

In practical terms, this means that after an object has been moved, it should be safe to call its destructor, assign a new value to it, or destroy it. However, the exact contents or validity of its internal data are not guaranteed. Most move constructors and move assignment operators set the source object’s pointers to null or reset members to default values to maintain safety.

It is generally unsafe to use a moved-from object without reinitializing or assigning to it. Accessing resources from a moved-from object may lead to undefined behavior if the internal pointers are null or invalid.

Programmers should treat moved-from objects as being in a “valid but unspecified” state, meaning the object is still usable but should be assigned a new value before use.

Designing move operations carefully to leave the source in a consistent and safe state is a best practice. This often means resetting pointers, clearing containers, or setting flags indicating emptiness.

Understanding and respecting the state of moved-from objects is essential for writing robust programs that utilize move semantics effectively.

Common Pitfalls and Best Practices with Move Semantics

While move semantics can greatly improve performance, improper use or misunderstanding can introduce bugs and subtle issues. One common pitfall is using an object after it has been moved from without properly reinitializing it. Since a moved-from object is left in a valid but unspecified state, accessing its resources without reassignment can lead to undefined behavior.

Another issue arises from inadvertent copying instead of moving. If a move constructor or move assignment operator is not implemented correctly, or if std::move is omitted when needed, the compiler may fall back on copying, causing unexpected performance degradation.

It is important to avoid moving from objects that are still needed later in the program or whose state matters after the move. Move semantics imply relinquishing ownership, so using moved-from objects without care can cause logic errors.

When implementing move constructors and move assignment operators, handling self-assignment and exception safety is critical. Self-assignment in the move assignment operator should be checked to avoid destroying resources inadvertently. Exception safety ensures that if an exception is thrown during a move, the program remains in a consistent state.

A good practice is to implement the move assignment operator using the “move-and-swap” idiom. This technique uses a temporary object and swaps its resources with the current object, simplifying resource management and improving safety.

Programmers should also remember the rule of five: if a class defines a destructor, copy constructor, copy assignment operator, move constructor, or move assignment operator, it should probably define all five to maintain consistent behavior.

Impact of Move Semantics on Standard Library Containers

The introduction of move semantics had a profound impact on the performance and design of standard library containers such as std::vector, std::string, std::map, and others.

Containers often manage dynamic memory and complex internal structures, so copying elements during resizing or reallocation could be very expensive. With move semantics, containers can transfer ownership of elements when resizing or rearranging, avoiding deep copies.

For example, when a vector resizes and moves its elements to a new memory block, it will use the move constructor if available, rather than copying each element. This results in significant speed improvements, especially for vectors holding large or complex objects.

Similarly, algorithms that rearrange elements, such as std::sort or std::stable_sort, benefit from move operations by moving elements instead of copying them.

Standard library containers have been updated to prefer move operations when possible, making modern C++ programs more efficient without requiring any changes to user code.

Additionally, containers like std::unique_ptr rely heavily on move semantics because they cannot be copied but can be moved, ensuring unique ownership of dynamically allocated resources.

Move Semantics and Exception Safety

Exception safety is an important consideration when implementing move semantics. Move constructors and move assignment operators should leave objects in a consistent, destructible state even if exceptions occur during the move.

The move constructor usually performs a shallow transfer of resources, which is unlikely to throw exceptions. However, if resources involve allocations or complex operations, exceptions might be thrown, so care must be taken.

In move assignment operators, releasing old resources and acquiring new ones should be done carefully to avoid resource leaks if an exception occurs mid-operation.

One strategy to ensure strong exception safety is the “move-and-swap” idiom. It involves creating a temporary object with the new resource, swapping its contents with the current object, and letting the temporary’s destructor release old resources. This approach guarantees that either the assignment completes successfully or the original object remains unchanged.

Ensuring exception safety in move operations contributes to robust and maintainable code, avoiding resource leaks and inconsistent states.

Final Thoughts

Move semantics represent a major advancement in C++ resource management and performance optimization. By enabling the transfer of resources rather than costly copying, move semantics reduce runtime overhead and memory usage, especially for large or complex objects.

Move constructors and move assignment operators allow objects to relinquish ownership safely and efficiently, while rvalue references and std::move provide the language features necessary to implement this mechanism.

Proper understanding and use of move semantics empower developers to write high-performance applications that make better use of system resources. Move semantics also influence the design of the standard library and user-defined classes, making modern C++ code faster, cleaner, and safer.

While move semantics introduce some complexity, such as managing the state of moved-from objects and ensuring exception safety, following best practices leads to robust implementations.

In modern software development, mastering move semantics is essential for anyone aiming to write efficient and effective C++ programs.