# What is ASIO `any_io_executor`? Understanding the Polymorphic Executor

> Discover asio::any_io_executor, a polymorphic executor offering a unified interface for all Asio I/O objects. Write generic code that works with any executor without template specialization.

- Repository: [chriskohlhoff/asio](https://github.com/chriskohlhoff/asio)
- Tags: deep-dive
- Published: 2026-07-12

---

**`asio::any_io_executor` is a type-erased polymorphic executor that provides a uniform interface for all I/O objects in the Asio library, enabling generic code to work with any concrete executor without template specialization.**

The `asio::any_io_executor` class in the [chriskohlhoff/asio](https://github.com/chriskohlhoff/asio) repository serves as the default executor type for sockets, timers, and streams. This polymorphic wrapper abstracts away concrete executor implementations while preserving the essential properties required for asynchronous operations.

## What is `any_io_executor`?

`any_io_executor` is a **polymorphic executor** built on top of the **Execution library**’s `execution::any_executor` template. It is pre-instantiated with a specific set of properties that every I/O object in Asio requires, living in [`include/asio/any_io_executor.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/any_io_executor.hpp).

The class inherits from a specialized `execution::any_executor` configured with seven standard properties:

| Property | Meaning |
|----------|---------|
| `execution::context_as_t<execution_context&>` | Allows querying for the associated execution context. |
| `execution::blocking_t::never_t` | Guarantees the executor never blocks by default. |
| `execution::prefer_only<execution::blocking_t::possibly_t>` | Can be preferred to allow blocking operations. |
| `execution::prefer_only<execution::outstanding_work_t::tracked_t>` | Can be preferred to track outstanding work. |
| `execution::prefer_only<execution::outstanding_work_t::untracked_t>` | Can be preferred to ignore work tracking. |
| `execution::prefer_only<execution::relationship_t::fork_t>` | Can be preferred to treat spawned tasks as forks. |
| `execution::prefer_only<execution::relationship_t::continuation_t>` | Can be preferred to treat spawned tasks as continuations. |

According to the source code at [lines 37–65](https://github.com/chriskohlhoff/asio/blob/master/include/asio/any_io_executor.hpp#L37-L65), the class definition is:

```cpp
class any_io_executor :
  public execution::any_executor<
      execution::context_as_t<execution_context&>,
      execution::blocking_t::never_t,
      execution::prefer_only<execution::blocking_t::possibly_t>,
      execution::prefer_only<execution::outstanding_work_t::tracked_t>,
      execution::prefer_only<execution::outstanding_work_t::untracked_t>,
      execution::prefer_only<execution::relationship_t::fork_t>,
      execution::prefer_only<execution::relationship_t::continuation_t>
    >
{
  // ...
};

```

## Implementation Details

When the library is compiled **without** the legacy TS executor (`ASIO_USE_TS_EXECUTOR_AS_DEFAULT`), the implementation resides in `include/asio/impl/any_io_executor.ipp`. This file contains the inline definitions for constructors, assignment operators, `swap`, and property-specific `require`/`prefer` overloads.

The header defines required traits (`execute_member`, `query_member`, `require_member`, `prefer_member`, `equality_comparable`) ensuring seamless integration with the Asio execution model.

## Key Capabilities

### Type Erasure and Uniform Interface

`any_io_executor` provides **type-erasure**, allowing it to hold any concrete executor that satisfies the required properties. This hides the concrete type behind a uniform interface, making it the default `Executor` template parameter for I/O classes like `basic_stream_socket`.

### Flexible Construction

The executor can be constructed from:
- A concrete executor (`any_io_executor exec(my_executor);`)
- Another `any_io_executor` (copy/move operations)
- An executor wrapped with `std::nothrow_t` for no-throw construction guarantees

The constructor implementation appears at [lines 90–100](https://github.com/chriskohlhoff/asio/blob/master/include/asio/any_io_executor.hpp#L90-L100) of the header file.

### Property Customization

The executor supports **customization points** via `asio::require` and `asio::prefer`, returning a new `any_io_executor` with modified properties. For example, requesting a possibly-blocking executor returns a new instance with that property applied.

## Practical Usage Examples

### Constructing from a Concrete Executor

You can wrap any concrete executor, such as one from an `io_context`, into the polymorphic type:

```cpp
#include <asio.hpp>

int main() {
  asio::io_context ctx;
  // ctx.get_executor() returns a concrete executor type
  asio::any_io_executor exec{ ctx.get_executor() };   // type-erased wrapper
}

```

### Using with Asynchronous Operations

Most I/O objects default their executor parameter to `any_io_executor`, enabling generic function signatures:

```cpp
#include <asio.hpp>

void async_echo(asio::any_io_executor exec) {
  asio::ip::tcp::socket sock(exec);
  // async_read_some uses the stored executor internally
  sock.async_read_some(asio::buffer(data),
      [&](std::error_code ec, std::size_t n) { /* handle completion */ });
}

```

As implemented in [`basic_stream_socket.hpp`](https://github.com/chriskohlhoff/asio/blob/main/basic_stream_socket.hpp) at [lines 36–40](https://github.com/chriskohlhoff/asio/blob/master/include/asio/basic_stream_socket.hpp#L36-L40), the template parameter defaults to `any_io_executor`.

### Modifying Executor Properties

Use `asio::require` or `asio::prefer` to obtain an executor with specific characteristics:

```cpp
#include <asio.hpp>

int main() {
  asio::io_context ctx;
  asio::any_io_executor exec{ ctx.get_executor() };

  // Request a possibly-blocking executor
  auto blk_exec = asio::require(exec, asio::execution::blocking.possibly);
}

```

The `require` implementation is located at [lines 25–33](https://github.com/chriskohlhoff/asio/blob/master/include/asio/any_io_executor.hpp#L25-L33).

### Integration with C++20 Coroutines

Coroutines can accept `any_io_executor` to remain agnostic of the specific execution context:

```cpp
#include <asio.hpp>
#include <asio/experimental/coro.hpp>

asio::awaitable<void> coro(asio::any_io_executor exec) {
  asio::ip::tcp::socket sock(exec);
  co_await sock.async_connect(asio::ip::tcp::endpoint{});
}

```

The coroutine uses whichever executor the caller provides, wrapped in the polymorphic interface.

## Summary

- **`any_io_executor`** is a type-erased polymorphic executor built on `execution::any_executor` with seven standard I/O properties.
- It resides in [`include/asio/any_io_executor.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/any_io_executor.hpp) with implementations in `include/asio/impl/any_io_executor.ipp`.
- It enables **type-erasure** of concrete executors while supporting property queries via `asio::require` and `asio::prefer`.
- Most I/O objects (sockets, timers, streams) default to `any_io_executor`, allowing generic code that works with any execution context.
- It supports construction from concrete executors, copy/move operations, and integration with C++20 coroutines.

## Frequently Asked Questions

### What is the difference between `any_io_executor` and concrete executors like `io_context::executor_type`?

`any_io_executor` is a type-erased wrapper that can hold any concrete executor satisfying the required properties, while `io_context::executor_type` is a specific concrete type tied to that execution context. The polymorphic executor allows writing functions that accept any executor without templating, whereas concrete executors provide compile-time type safety and potentially better optimization.

### When should I use `any_io_executor` versus a template parameter?

Use `any_io_executor` when writing generic code that must accept any executor type without template proliferation, such as library interfaces or coroutine functions. Use template parameters with concrete executor types when you need compile-time optimization and type safety, or when the specific executor type's unique capabilities must be preserved.

### Does `any_io_executor` incur performance overhead compared to concrete executors?

Yes, `any_io_executor` incurs a small runtime overhead due to type-erasure and virtual dispatch through the `execution::any_executor` base class. However, this overhead is typically minimal for I/O-bound operations. For CPU-intensive work requiring maximum performance, concrete executor types and templates provide better optimization opportunities.

### How do I convert an `any_io_executor` back to a concrete executor type?

You cannot directly extract the concrete type from an `any_io_executor` due to type-erasure. If you need the concrete type, you must use `asio::query` with `asio::execution::context` to obtain the underlying `execution_context`, then cast it to the specific context type (e.g., `io_context`), and finally call `get_executor()` on that context to retrieve the concrete executor type.