Habrá un largo muro de texto, con un tipo de pensamientos al azar. Ideas clave
- En C ++, el tiempo de compilación es muy importante,
- Construir rendimiento sin optimizaciones también es importante,
- La carga cognitiva es aún más importante. No discutiré particularmente este punto, pero si el lenguaje de programación me hace sentir tonto, es poco probable que lo use, mucho menos me encanta. C ++ hace esto conmigo constantemente .
La publicación del blog Standard Ranges de Eric Niebler sobre rangos C ++ 20 recientemente rodeó todo el universo de Twitter, acompañado de un montón de comentarios no tan halagadores (por decirlo suavemente) sobre el estado de C ++ moderno.

Incluso yo he contribuido ( enlace ):
Este ejemplo de tripta pitagórica en rangos C ++ 20, en mi opinión, parece monstruoso. Y sí, entiendo que los rangos pueden ser útiles, las proyecciones pueden ser útiles, etc. Sin embargo, el ejemplo es espeluznante. ¿Por qué alguien necesitaría esto?
Echemos un vistazo más de cerca a todo esto debajo del corte.
Todo esto se salió un poco de control (¡incluso una semana después, los comentarios continuaron volando hacia este árbol de hilos!).
Ahora, tengo que disculparme con Eric por comenzar con su artículo; mi grito de Yaroslavna se centrará principalmente en la "condición general de C ++". Un puñado de tipos de gamedev amargados hace un año se topó con una explicación de Boost.Geometry de la misma manera, y lo mismo sucedió con docenas de otros aspectos del ecosistema C ++.
Pero ya sabes, Twitter no es el mejor lugar para conversaciones sensibles, etc., etc. ¡Tendremos que expandir el pensamiento aquí y ahora!
El pitagórico se triplica al estilo de los rangos C ++ 20
Guarde el texto de ejemplo completo de la publicación de Eric:
// C++20.
// N .
#include <iostream>
#include <optional>
#include <ranges> // !
using namespace std;
// maybe_view 0 1
template<Semiregular T>
struct maybe_view : view_interface<maybe_view<T>> {
maybe_view() = default;
maybe_view(T t) : data_(std::move(t)) {
}
T const *begin() const noexcept {
return data_ ? &*data_ : nullptr;
}
T const *end() const noexcept {
return data_ ? &*data_ + 1 : nullptr;
}
private:
optional<T> data_{};
};
// "for_each" ,
// ,
// .
// ( constrained lambdas C++20.)
inline constexpr auto for_each =
[]<Range R,
Iterator I = iterator_t<R>,
IndirectUnaryInvocable<I> Fun>(R&& r, Fun fun)
requires Range<indirect_result_t<Fun, I>> {
return std::forward<R>(r)
| view::transform(std::move(fun))
| view::join;
};
// "yield_if" bool ,
// 0 1 .
inline constexpr auto yield_if =
[]<Semiregular T>(bool b, T x) {
return b ? maybe_view{std::move(x)}
: maybe_view<T>{};
};
int main() {
// :
using view::iota;
auto triples =
for_each(iota(1), [](int z) {
return for_each(iota(1, z+1), [=](int x) {
return for_each(iota(x, z+1), [=](int y) {
return yield_if(x*x + y*y == z*z,
make_tuple(x, y, z));
});
});
});
// 10
for(auto triple : triples | view::take(10)) {
cout << '('
<< get<0>(triple) << ','
<< get<1>(triple) << ','
<< get<2>(triple) << ')' << '\n';
}
}
, , «Getting Lazy with C++», N :
void printNTriples(int n)
{
int i = 0;
for (int z = 1; ; ++z)
for (int x = 1; x <= z; ++x)
for (int y = x; y <= z; ++y)
if (x*x + y*y == z*z) {
printf("%d, %d, %d\n", x, y, z);
if (++i == n)
return;
}
}
:
, . , , ? , ?
(list comprehensions) . , - , C++ , - Haskell . C++20 , . .
C++
, , C/C++ («» — , «, », ). , :
// simplest.cpp
#include <time.h>
#include <stdio.h>
int main()
{
clock_t t0 = clock();
int i = 0;
for (int z = 1; ; ++z)
for (int x = 1; x <= z; ++x)
for (int y = x; y <= z; ++y)
if (x*x + y*y == z*z) {
printf("(%i,%i,%i)\n", x, y, z);
if (++i == 100)
goto done;
}
done:
clock_t t1 = clock();
printf("%ims\n", (int)(t1-t0)*1000/CLOCKS_PER_SEC);
return 0;
}
: clang simplest.cpp -o outsimplest
. 0.064 , 8480 , 2 ( : 2018 MacBookPro; Core i9 2.9GHz; — Xcode 10 clang).
(3,4,5)
(6,8,10)
(5,12,13)
(9,12,15)
(8,15,17)
(12,16,20)
(7,24,25)
(15,20,25)
(10,24,26)
...
(65,156,169)
(119,120,169)
(26,168,170)
! , («Debug») ; («Release»): clang simplest.cpp -o outsimplest -O2
. 0.071 (8480 ), 0 ( , clock()
).
, , . « » ( , «» « » ). , , -, N , .
— , , . :
// simple-reusable.cpp
#include <time.h>
#include <stdio.h>
struct pytriples
{
pytriples() : x(1), y(1), z(1) {}
void next()
{
do
{
if (y <= z)
++y;
else
{
if (x <= z)
++x;
else
{
x = 1;
++z;
}
y = x;
}
} while (x*x + y*y != z*z);
}
int x, y, z;
};
int main()
{
clock_t t0 = clock();
pytriples py;
for (int c = 0; c < 100; ++c)
{
py.next();
printf("(%i,%i,%i)\n", py.x, py.y, py.z);
}
clock_t t1 = clock();
printf("%ims\n", (int)(t1-t0)*1000/CLOCKS_PER_SEC);
return 0;
}
. 168 , .
pytriples
, next()
; , . , .
, , for- ,
, , . , , ( ), .
C++ - , , , , «» ( «Ranges, Code Quality, and the Future of C++»); ( , C++ ):
generator<std::tuple<int,int,int>> pytriples()
{
for (int z = 1; ; ++z)
for (int x = 1; x <= z; ++x)
for (int y = x; y <= z; ++y)
if (x*x + y*y == z*z)
co_yield std::make_tuple(x, y, z);
}
C++
C++20 ? , :
auto triples =
for_each(iota(1), [](int z) {
return for_each(iota(1, z+1), [=](int x) {
return for_each(iota(x, z+1), [=](int y) {
return yield_if(x*x + y*y == z*z,
make_tuple(x, y, z));
});
});
});
. , , , . , C++ , , C++ (« ? , !») — . return- , , , .
, , .
, , , C++, , :
template<Semiregular T>
struct maybe_view : view_interface<maybe_view<T>> {
maybe_view() = default;
maybe_view(T t) : data_(std::move(t)) {
}
T const *begin() const noexcept {
return data_ ? &*data_ : nullptr;
}
T const *end() const noexcept {
return data_ ? &*data_ + 1 : nullptr;
}
private:
optional<T> data_{};
};
inline constexpr auto for_each =
[]<Range R,
Iterator I = iterator_t<R>,
IndirectUnaryInvocable<I> Fun>(R&& r, Fun fun)
requires Range<indirect_result_t<Fun, I>> {
return std::forward<R>(r)
| view::transform(std::move(fun))
| view::join;
};
inline constexpr auto yield_if =
[]<Semiregular T>(bool b, T x) {
return b ? maybe_view{std::move(x)}
: maybe_view<T>{};
};
, - , - , Perl , Brainfuck — , . C++ 20 . , , , .
, , maybe_view
, for_each
, yield_if
— « », ; , … .
« »
:
, , C++, , .
C++20 , , range-v3 ( ), .
// ranges.cpp
#include <time.h>
#include <stdio.h>
#include <range/v3/all.hpp>
using namespace ranges;
int main()
{
clock_t t0 = clock();
auto triples = view::for_each(view::ints(1), [](int z) {
return view::for_each(view::ints(1, z + 1), [=](int x) {
return view::for_each(view::ints(x, z + 1), [=](int y) {
return yield_if(x * x + y * y == z * z,
std::make_tuple(x, y, z));
});
});
});
RANGES_FOR(auto triple, triples | view::take(100))
{
printf("(%i,%i,%i)\n", std::get<0>(triple), std::get<1>(triple), std::get<2>(triple));
}
clock_t t1 = clock();
printf("%ims\n", (int)(t1-t0)*1000/CLOCKS_PER_SEC);
return 0;
}
0.4.0 (9232b449e44
22 2018 ), clang ranges.cpp -I. -std=c++17 -lc++ -o outranges
. 2.92 , 219 , 300 .
, . (clang ranges.cpp -I. -std=c++17 -lc++ -o outranges -O2
) 3.02 , 13976 , 1 . , , .
.
— C++
2.85 , « C++».
, « 3 » — , . CPU . , clang (SQLite) , 220 ? 0.9 . , 5 ?
++ , . ? , - (Chromium, Clang/LLVM, UE4, ). , C++, , , , . , , C++ , , , , .
« » #include
, . , C++ /.
range-v3 1.8 , ! , 30 , 102 . « C++» 720 .
/ ! — , . . precompiled header (pch.h : #include <range/v3/all.hpp>
, pch.h, PCH: clang -x c++-header pch.h -I. -std=c++17 -o pch.h.pch
, pch: clang ranges.cpp -I. -std=c++17 -lc++ -o outranges -include-pch pch.h.pch
). 2.24 . , PCH 0.7 . 2.1 , , C++ :(
—
150 . , 2 3 . , 10 . , ? ?
, , , . ; , ( ). , , , , 10 100 , «». , . , , «» «». , 2 .
, (-O2
clang) « C++»… , , «zero cost abstractions», - . , .
— ! , , . , . - , . , , .
Arseny Kapoulkine «Optimizing OBJ loader» YouTube, , 10 , STL (). () , STL Microsoft .
, «STL — »; STL, (EASTL libc++ ), - Microsoft STL , « ».
, , ! — «STL », , - . (, STL, , C++, ).
« » C#:
using System;
using System.Diagnostics;
using System.Linq;
class Program
{
public static void Main()
{
var timer = Stopwatch.StartNew();
var triples =
from z in Enumerable.Range(1, int.MaxValue)
from x in Enumerable.Range(1, z)
from y in Enumerable.Range(x, z)
where x*x+y*y==z*z
select (x:x, y:y, z:z);
foreach (var t in triples.Take(100))
{
Console.WriteLine($"({t.x},{t.y},{t.z})");
}
timer.Stop();
Console.WriteLine($"{timer.ElapsedMilliseconds}ms");
}
}
, . C#:
var triples =
from z in Enumerable.Range(1, int.MaxValue)
from x in Enumerable.Range(1, z)
from y in Enumerable.Range(x, z)
where x*x+y*y==z*z
select (x:x, y:y, z:z);
C++:
auto triples = view::for_each(view::ints(1), [](int z) {
return view::for_each(view::ints(1, z + 1), [=](int x) {
return view::for_each(view::ints(x, z + 1), [=](int y) {
return yield_if(x * x + y * y == z * z,
std::make_tuple(x, y, z));
});
});
});
, . ? , C# LINQ :
var triples = Enumerable.Range(1, int.MaxValue)
.SelectMany(z => Enumerable.Range(1, z), (z, x) => new {z, x})
.SelectMany(t => Enumerable.Range(t.x, t.z), (t, y) => new {t, y})
.Where(t => t.t.x * t.t.x + t.y * t.y == t.t.z * t.t.z)
.Select(t => (x: t.t.x, y: t.y, z: t.t.z));
C#? Mac, Mono ( C#) 5.16 mcs Linq.cs
0.20 . « C#» 0.17 .
, LINQ 0.03 . 3 C++ — 100 !
, ?
, - .
, Unity , « Boost ». , , , - Boost.Asio, , , asio.h
<windows.h>
, .
STL. , , EASTL — , /, , . - , (unordered_map
STL , separate chaining; - ). .
.
, ( , ) , «» C++ , ( ! ). , « » , ( , ! ).
, ( ) , , « » . () , , (ノಥ益ಥ)ノ ┻━┻, «, , ». : «, - ?». : «, !». (, , - ). , C C++, , . ; , « — », — ; .
, C++, . , « — », « , , ».
C++?
. , « , , ». , C++ , , !
- , , C++ «» .
C/C++. , - 16 . Boost, : «, , !». , .
, , Formula 1 . ? . ? . , ? ! 99% , ? .
:
, . « ». , C++ 1) 2) , , , , , . 2.
, , C++ , , . . ( ) . ( EASTL). , C++ , (Jai) (Rust, C#).
, , « » , — . , , , , , , . ; , - , — . !
, - , C++, STL . - , .
, . — .
, , , . , , , , , .
, - , ? «» « », . , ? // — . , «, », .
- « , , » — !
- « , , , » — !
- «, » — !
- « , , , » — , , .
« , , C++», . , « Boost!» C++, , Boost — - .
, , . C C++ , ( Rust, ). C++, , , , , .
, , — . C++, « » ( - ); - . , ; , . , , - , «C++ — , », , .
, (C++11/14/17) C++ — , , constexpr if
, . , , STL ( , ), , , ///… , , , .
. 19-20 C++ Russia 2019. , . — Arno Schödl, CTO Think-Cell, «Text Formatting For a Future Range-Based Standard Library». , ? . , ( ).