Il y aura un long mur de texte, avec un type de pensées aléatoires. Idées clés:
- En C ++, le temps de compilation est trĂšs important,
- Construire des performances sans optimisations est également important,
- La charge cognitive est encore plus importante. Je ne discuterai pas particuliĂšrement de ce point, mais si le langage de programmation me fait me sentir stupide, il est peu probable que je lâutilise, encore moins lâaime. C ++ le fait constamment avec moi.
Le blog d'Eric Niebler Standard Ranges sur les gammes C ++ 20 a récemment encerclé l'univers Twitter entier, accompagné d'un tas de commentaires pas si flatteurs (pour le dire en douceur!) à propos de l'état du C ++ moderne.

MĂȘme moi, j'ai contribuĂ© ( lien ):
Cet exemple de triplets de Pythagore sur les rangs C ++ 20, Ă mon avis, semble monstrueux. Et oui, je comprends que les plages peuvent ĂȘtre utiles, les projections peuvent ĂȘtre utiles, etc. Cependant, l'exemple est effrayant. Pourquoi aurait-on besoin de ça?
Examinons de plus prĂšs tout cela sous la coupe.
Tout cela est devenu un peu hors de contrĂŽle (mĂȘme une semaine plus tard, les commentaires ont continuĂ© Ă voler dans cet arbre de fils!).
Maintenant, je dois m'excuser auprĂšs d'Eric d'avoir commencĂ© avec son article; mon cri de Yaroslavna portera principalement sur «l'Ă©tat gĂ©nĂ©ral du C ++». Une poignĂ©e de mecs gamedev aigris il y a un an a expliquĂ© l' explication de Boost.Geometry de la mĂȘme maniĂšre, et la mĂȘme chose s'est produite avec des dizaines d'autres aspects de l'Ă©cosystĂšme C ++.
Mais vous savez, Twitter n'est pas le meilleur endroit pour des conversations sensibles, etc., etc. Nous devrons développer la pensée ici et maintenant!
Triplets pythagoriciens dans le style des rangs C ++ 20
Conservez le texte d'exemple complet du post d'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». , ? . , ( ).