Ú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¶
- Seis métodos para dividir cadenas en C++ | C++那些事
- Notas sobre el desarrollo de extensiones de CPython | C++那些事
- Chuleta de desarrollo con libcurl en C++ | C++那些事
- Cómo leer código C++ de código abierto | C++那些事
- Un fragmento de código C++ interesante | C++那些事