Translate

jueves, 3 de septiembre de 2026

El operador | en C++23: construyendo pipelines



En C++, el operador | siempre existió.

Tradicionalmente lo conocemos como el operador OR a nivel de bits:

int result = 5 | 3;


Pero con Ranges, el operador | también permite construir pipelines de transformaciones.


Por ejemplo:

#include <ranges>

#include <vector>

#include <iostream>


int main() {

    std::vector<int> numbers = {1, 2, 3, 4, 5};


    auto result = numbers

        | std::views::filter([](int x) {

            return x % 2 == 0;

        })

        | std::views::transform([](int x) {

            return x * 10;

        });


    for (auto value : result) {

        std::cout << value << " ";

    }

}


Resultado:

20 40


Podemos leer este código de izquierda a derecha:

numbers

   |

filter

   |

transform


Primero:

[1, 2, 3, 4, 5]


Aplicamos:

filter(x -> x es par)


Resultado:

[2, 4]


Luego:

transform(x -> x * 10)


Resultado:

[20, 40]


El código:

numbers

    | std::views::filter(...)

    | std::views::transform(...);


representa un pipeline.


Cada operación recibe el resultado de la operación anterior.

El operador | sigue siendo el mismo operador definido por C++.

Lo que cambia es que la biblioteca de Ranges utiliza operator overloading para darle un nuevo comportamiento cuando trabajamos con vistas.


Conceptualmente:

numbers | operation


es similar a escribir:

operation(numbers)


Por ejemplo:

numbers

    | std::views::filter(isEven)


podemos imaginarlo como:

std::views::filter(numbers, isEven);


La ventaja del operador | es la legibilidad.


Comparemos:

auto result =

    std::views::transform(

        std::views::filter(numbers, isEven),

        multiplyByTen

    );


Con:

auto result =

    numbers

    | std::views::filter(isEven)

    | std::views::transform(multiplyByTen);


Ambas expresan la misma idea, pero el pipeline permite leer el código en el orden en que ocurren las transformaciones.


Este estilo puede resultar familiar para quienes trabajan con otros lenguajes.


Por ejemplo, en F#:

data

|> filter

|> transform


O en Elixir:

data

|> filter()

|> transform()


En C++:


data

    | std::views::filter(...)

    | std::views::transform(...);


La idea es similar:

dato → operación → operación → resultado


En un post anterior vimos que C++23 no tiene una operación llamada flatMap.

Pero podemos construirla con un pipeline:

auto result = numbers

    | std::views::transform([](int x) {

        return std::vector{x, x * 10};

    })

    | std::views::join;


Podemos leerlo como:


numbers

   |

transform

   |

join


Es decir: flatMap = transform + join

C++23 no se convirtió en un lenguaje funcional.


Pero con Ranges y Views, C++ permite escribir código mucho más declarativo y expresar transformaciones sobre datos como pipelines.


Y quizás esa sea una de las características más interesantes del C++ moderno.


No hay comentarios.:

Publicar un comentario