Saltar a contenido

Última actualización:2026-10-12

Imprimir 1-100 en C++ sin bucles

Te planteo una condición: sin usar bucles, imprime los números del 1 al 100.

Con bucles sería muy sencillo; ¿cuántos métodos se te ocurren?

Hoy os muestro cómo escribir este código con algunos trucos sofisticados de C++; se aceptan comentarios y reenvíos~

Este ejercicio es en realidad muy sencillo, pero cubre muchos puntos de entrevista, entre ellos:

  • Especialización y semi-especialización de plantillas, cómo escribir plantillas recursivas
  • CRTP
  • Cómo usar std::copy, std::iota y std::transform, e implementación de cada algoritmo
  • Variables estáticas de clase
  • Etcétera

1. Recursión

1-100 puede descomponerse en 5*5*4; se usan tres funciones para imprimirlo: la función a hace cout una vez y luego incrementa la variable en 1; la función b anida a en cadena 5 veces; la función c anida b en cadena 5 veces; y por último, en la función main, se anida c en cadena 4 veces.

#include <iostream>

int a(int n) {
  std::cout << n++ << std::endl;
  return n;
}

int b(int n) { return a(a(a(a(a(n))))); }

int c(int n) { return b(b(b(b(b(n))))); }

int main() {
  c(c(c(c(1))));
  return 0;
}

2. Sobrecarga de plantillas

Se usan plantillas y especialización para tratarlo: al iterar hasta 100 solo se imprime 100; en caso contrario se imprime I y se continúa recursivamente con +1.

#include <iostream>
template <int I>
struct JustPrint : public JustPrint<I + 1> {
  JustPrint() : JustPrint<I + 1>() { std::cout << I << " "; }
};

template <>
struct JustPrint<100> {
  JustPrint<100>() { std::cout << 1 << " "; }
};

int main() { JustPrint<1> j; }

3. CRTP

El método 3 se basa en el método 2, reconvertido al estilo de programación crtp; ya te va pareciendo un poco incomprensible, ¿eh, jeje?

El struct siguiente puede sustituirse por una clase; aquí lo describo con clases. En realidad son dos clases: la primera, Print, es la clase padre, imprime la I de cada vez y llama a la clase hija; la clase hija, PrintDerived, hereda de Print y se pasa a sí misma a la clase padre; la clase hija es una clase especializada que controla el fin de la recursión.

#include <iostream>

template <typename Derived, int I>
struct Print {
  Print() {
    std::cout << I << " ";
    Derived::next();
  }
};

template <int I>
struct PrintDerived : public Print<PrintDerived<I>, I> {
  static void next() { PrintDerived<I + 1>(); }
};
template <>
struct PrintDerived<100> : public Print<PrintDerived<100>, 100> {
  static void next() {}
};

int main() {
  PrintDerived<1> j;
  return 0;
}

4. Variables estáticas de clase

Se crea un array Test de 100 elementos; la variable estática se acumula en cada ocasión.

#include <iostream>
using namespace std;

class Test {
  static int i;

 public:
  Test() { cout << ++i << endl; }
};
int Test::i;
int main() {
  Test a[100];
  return 0;
}

5. Usar la biblioteca de algoritmos

Usar std::for_each, simple y directo.

int main() {
  std::vector<int> l(100);
  std::iota(l.begin(), l.end(), 1);
  std::for_each(l.begin(), l.end(), [](int& n) { std::cout << n << std::endl; });
  return 0;
}

6. ranges de C++20

Usar el std::views de C++20, imprimiendo al hacer std::copy.

#include <iostream>
#include <iterator>
#include <ranges>
int main() {
  auto numbers = std::views::iota(1, 100 + 1);
  std::copy(numbers.begin(), numbers.end(), std::ostream_iterator<int>(std::cout, "\n"));
}

Este método puede reconvertirse un poco más: hay otra implementación que usa std::transform.

auto range = std::views::transform(std::ranges::iota_view{1, 101}, [](const auto& x) {
  std::cout << x << std::endl;
  return x;
});
std::vector<int> a(range.begin(), range.end());

Temas relacionados

Comentarios