Open In App

C++23 – <expected> Header

Last Updated : 22 Sep, 2023
Comments
Improve
Suggest changes
Like Article
Like
Report

C++23, the next major version of the C++ programming language, brings several exciting features and enhancements. One of the significant additions in C++23 is the <expected> header, which aims to improve error handling and make code more robust and readable. In this article, we will explore the feature of <expected> header, its purpose, and how it simplifies error handling in C++.

Problem with Error Handling

Error handling in C++ has historically been a complex and error-prone task. C++ has exceptions and error codes, but choosing the right approach and implementing error handling consistently across a codebase can be challenging. Additionally, exceptions can introduce performance overhead in certain scenarios.

<expected> Header in C++

The <expected> header in C++23 introduces a new way to handle errors and expected values. It is inspired by similar constructs in other programming languages, such as Rust’s Result and Swift’s Result types. The primary goal of <expected> is to provide a more explicit and structured way to handle expected values and errors without relying solely on exceptions or error codes.

The <expected> header introduces two main class templates:

  1. std::expected
  2. std::unexpected

Let’s dive into each of them.

1. std::expected Class Template

The std::expected is a class template in C++ that serves as a mechanism for managing functions that may return either a valid result or an error. It is a wrapper that is particularly useful in scenarios where exceptions might not be the preferred error-handling approach.

Syntax

The std::expected is used as the value return by the function as shown below:

std::expected<T, E> function_name {
        // statements
}

where,

  • T: This parameter represents the type of the expected (valid) value that the function may return when it succeeds.
  • E: This parameter represents the type of error condition that the function may return when it fails.

When a function returns an instance of std::expected, the caller can easily check whether the result is valid (contains a value of type T) or represents an error (contains an error of type E). This approach provides a clear and structured way to handle errors in a functional, non-exception-based manner.

std::expected Member Functions

Following are some commonly used Member functions of the std::expected class:

S.No.

Function

Description

1

value() It allows you to retrieve the stored value of type T.

2

error() This one facilitates access to the stored error of type E.

3

has_value(): This member function is used to inquire whether the std::expected contains a value or not. It returns true if the std::expected holds a value and false if it holds an error.

4

error_code() When applicable, this member function is employed to convert the stored error into an error code.

2. std::unexpected Class Template

The std::unexpected is not a standalone class but rather a concept used with std::expected. It helps to define how unexpected errors are handled within a specific context.

When an unexpected error occurs within a function that returns a std::expected, the function can use std::unexpected to specify what action should be taken. This allows developers to define a custom response to unexpected errors.

Syntax

std::expected<T, E> function_name {
        // statements
        return std::unexpected< E >(some_value);
}

where,

  • E: The error type specified in std::expected template.

How to use std::expected?

To use the std::expected class template, follow the given steps:

  1. Create an instance of std::expected with the expected value type (T) and the error type (E).
  2. In case of an error, return an instance of std::expected with the error type (E).
  3. When an operation succeeds, return an instance of std::expected with the expected value (T).
  4. Utilize methods like has_value() and value() to access the value or error() to obtain the error, as needed.

Examples

Below given is the example code that uses expected in C++:

Example 1: Handling a Successful Division

C++




// C++ program to illustrate the successful execution using
// std::expected feature
#include <expected>
#include <iostream>
using namespace std;
  
// defining function to return the std::expected object
expected<int, string> divide(int a, int b)
{
    if (b == 0) {
        // return value in case of exception
        return unexpected<string>("Division by zero");
    }
    // return value when execution is successful
    return a / b;
}
  
// driver code
int main()
{
    auto result = divide(10, 2);
  
    // printing result based on the value of std::expected
    if (result.has_value()) {
        cout << "Result: " << result.value() << endl;
    }
    else {
        cerr << "Error: " << result.error() << endl;
    }
  
    return 0;
}


Output

Result: 5

In this example, the divide function returns a std::expected<int, std::string>, representing the result of division or an error message for division by zero.

Example 2: Handling Division by Zero

C++




// C++ program to illustrate the successful execution using
// std::expected feature
#include <expected>
#include <iostream>
using namespace std;
  
// defining function to return the std::expected object
expected<int, string> divide(int a, int b)
{
    if (b == 0) {
        // return value in case of exception
        return unexpected<string>("Division by zero");
    }
    // return value when execution is successful
    return a / b;
}
  
// driver code
int main()
{
    auto result = divide(10, 0);
  
    // printing result based on the value of std::expected
    if (result.has_value()) {
        cout << "Result: " << result.value() << endl;
    }
    else {
        cerr << "Error: " << result.error() << endl;
    }
  
    return 0;
}


Output

Error: Division by zero

Example 3: Using a Different Error Type

C++




// C++ program to illustrate the behaviour of std::expected
// for different type
#include <expected>
#include <iostream>
using namespace std;
  
expected<int, float> divideWithFloatError(int a, int b)
{
    if (b == 0) {
        // Different error type
        return unexpected<float>(0.0f);
    }
    return a / b;
}
  
// Driver code
int main()
{
    auto result3 = divideWithFloatError(6, 0);
    if (result3.has_value()) {
        cout << "Result 3: " << result3.value() << endl;
    }
    else {
        cerr << "Error 3: " << result3.error() << endl;
    }
  
    return 0;
}


Output

Error 3: 0

Advantages of using std::expected

The <expected> header brings forth a multitude of benefits:

  1. std::expected makes error handling simple and concise within code. It distinctly separates the expected and error scenarios.
  2. The C++ type system ensures that you cannot inadvertently mix up expected values and errors.
  3. std::Expected is designed with minimal performance overhead, ensuring efficient error handling.

Conclusion

The <expected> header in C++23 represents a significant leap forward in error handling for C++ developers. With improved code clarity, type safety, and performance characteristics, it offers an efficient solution for handling expected values and errors gracefully.
 



Next Article
Article Tags :
Practice Tags :

Similar Reads