289 lines
15 KiB
Plaintext
289 lines
15 KiB
Plaintext
extends ../templates/main.jade
|
||
|
||
block title
|
||
| C++ | Language Criticism | xigoi
|
||
|
||
block content
|
||
h1 C++ Language Criticism
|
||
p
|
||
| Everything that applies to
|
||
a(href="c.html") C
|
||
| also applies to C++. This article contains only things that don't apply to C.
|
||
p
|
||
| The
|
||
a(href="http://yosefk.com/c++fqa/index.html") C++ FQA
|
||
| has a lot of great points, so go read it too.
|
||
h2 Overall philosophy
|
||
p I believe that a programming language should be designed to make simple things simple and complex things as simple as possible. C++ is designed to make simple things complex and complex things even more complex. For example, a program to read space-separated numbers from STDIN, sort them and again output them space-separated:
|
||
.comparison
|
||
.compared
|
||
h6 Python 3
|
||
pre
|
||
code.
|
||
nums = [int(inp) for inp in input().split()]
|
||
print(*sorted(nums))
|
||
a(href="https://tio.run/##K6gsycjPM/7/P680t1jBViE6M69EIzOvQFMhLb9IAcgAYhBVWqKhqVdckJMJpGO5CopAyrSK84tKUlM0QFo1Nf//NzY0UTA0VbBUMDJTMFUwNgUA") Try it online!
|
||
.compared
|
||
h6 Nim
|
||
pre
|
||
code.
|
||
import std/[strutils, sequtils, algorithm]
|
||
|
||
let nums = stdin.readLine.splitWhitespace.mapIt(it.parseInt)
|
||
echo nums.sorted.mapIt($it).join(" ")
|
||
a(href="https://tio.run/##LY3LCsIwEADv@YqleGhBIrVG6MEPKHj3IB5Cs9iVvMxuvz8@6G0OM0ykUCuFnIoAizvcWcoq5HkPjO@NrH@mQrKEh1IeBeIaGC4/n6IuaN2VImrOnuS2kCBnO6MONk/SkuhsC@MUpVM4L@lfa/4O0W3OjqTTr0SxbaDpah36E/QGRjiewcBgPg") Try it online!
|
||
.compared
|
||
h6 Haskell
|
||
pre
|
||
code.
|
||
import Data.List
|
||
|
||
main = interact $ (++ "\n") . unwords . map show
|
||
. sort
|
||
. map (read :: String -> Int) . words
|
||
a(href="https://tio.run/##bcwxC8IwFATgPb/iKA6VYqDWCBZ0chHcXF0eNthg81KSJ/35sXUUbzoO7uspveww5Oz8GKLgTEL66pIo5ckxjnAsNtJDsEJZVSjuXKyh8eYpxC7NzdOI1IdJ4ScaaSb/zMujjJY6tC1uEh0/sTnhwrLIXzfnpt6hNjhgu4dBYz4") Try it online!
|
||
.compared
|
||
h6 C++
|
||
pre
|
||
code.
|
||
#include <iostream>
|
||
#include <vector>
|
||
#include <algorithm>
|
||
|
||
int main() {
|
||
std::vector<int> nums;
|
||
std::string inp;
|
||
while (std::getline(std::cin, inp, ' ')) {
|
||
nums.emplace_back(std::stoi(inp));
|
||
}
|
||
std::sort(nums.begin(), nums.end());
|
||
bool first = true;
|
||
for (int num : nums) {
|
||
if (first) {
|
||
first = false;
|
||
} else {
|
||
std::cout << ' ';
|
||
}
|
||
std::cout << num;
|
||
}
|
||
std::cout << std::endl;
|
||
}
|
||
a(href="https://tio.run/##ZY/NTsMwEITveYqRONSRAlIpQSINfRXkOE66wrEj24ED6rMH2/kBxM2e@WZ2V4zjfS/EPN@RFmpqJWoyzlvJh0v2o31I4Y39rXDVG0v@GrCMtMfASbMcXxngfFtVS6IO1gV6Gtx5M0I56R6kxyh9XklJsOT00ivScvkI0kWEChxwyJdipKYHOYyKC/nWcPHO1k5DLMB5Hjtv@yhjPUuRRvZxvWIt0C1b0MYYhY6s83iFt5OMYmcsWLwpwKhSZJtPHVjCNwF7uuPKpXhYADK8d2K5x0wedR2vWaHsnxcm/dl/09MnLK3O2W2eT8cnHEu84PEZJU7lNw") Try it online!
|
||
h2 Features
|
||
ul
|
||
li Despite being a very large language, C++ doesn't have basic features like sum types or pattern matching.
|
||
li Many features duplicate other features, adding their own advantages and drawbacks (and more unnecessary syntactic rules to learn). Examples: initializer lists,
|
||
code typedef
|
||
| /
|
||
code using
|
||
| ,
|
||
code #define
|
||
| /
|
||
code constexpr
|
||
| ,
|
||
code char[]
|
||
| /
|
||
code std::string
|
||
| ,
|
||
code int[]
|
||
| /
|
||
code std::array<int>
|
||
| ,
|
||
code printf
|
||
| /
|
||
code cout
|
||
| ,
|
||
code struct
|
||
| /
|
||
code class
|
||
h2 Syntax
|
||
p C++ took the horrible syntax of C and somehow managed to make it even worse.
|
||
ul
|
||
li The
|
||
a(href="https://en.wikipedia.org/wiki/Most_vexing_parse") most vexing parse
|
||
| (and similar rules). I can't fathom what could possibly cause someone to think this is a good idea.
|
||
li
|
||
a(href="https://blog.reverberate.org/2013/08/parsing-c-is-literally-undecidable.html") Parsing C++ is literally undecidable.
|
||
li Using less-than and greater-than signs as brackets, which hinders auto-pairing and complicates parsing while also looking ugly.
|
||
a(href="https://soc.me/languages/stop-using-for-generics") Here's a longer explanation from another person.
|
||
li The semicolon after a
|
||
code struct
|
||
| /
|
||
code class
|
||
| definition. Just why?
|
||
li Keywords that have multiple meanings depending on where you use them:
|
||
code static
|
||
| ,
|
||
code using
|
||
| ,
|
||
code typename
|
||
| .
|
||
li The keyword
|
||
code const
|
||
| is used to declare immutable variables (not constants, for which there is
|
||
code constexpr
|
||
| ) and it can be placed in various positions inside a type, which completely changes its meaning.
|
||
li Who decided that
|
||
code ::
|
||
| is a good path separator?
|
||
h2 Standard library
|
||
ul
|
||
li Despite being quite extensive, the standard library doesn't have basic things like
|
||
ul
|
||
li basic functions for working with strings (split, join)
|
||
li functional abstractions (map, filter, fold, …) [C++20 partially solves this with “range adaptors” (what a weird name), but the usage is unbearably cumbersome]
|
||
li optional/result types
|
||
li Using the term “vector” for a resizable array, even though the word has a completely different meaning in mathematics.
|
||
li There is no convenient way to pass a whole array/vector/list to a function, you have to do something like
|
||
code std::sort(arr.begin(), arr.end())
|
||
| .
|
||
li Weird function names: from C crypticisms (
|
||
code stoi
|
||
| ,
|
||
code fscanf
|
||
| ) to unusual words (
|
||
code emplace_back
|
||
| ).
|
||
li Idiomatically, it's required to write
|
||
code std::
|
||
| between everything from the standard library, which makes the code repetitive and unreadable. Who would have guessed that
|
||
code sort
|
||
| is a standard library function if it wasn't for the prefix?
|
||
h2 Tooling
|
||
ul
|
||
li Error messages.
|
||
ul
|
||
li If you try to search a “vector” (array) of a wrong type…
|
||
p Credit goes to
|
||
a(href="https://codegolf.stackexchange.com/a/10470/98955") this StackExchange answer
|
||
| .
|
||
pre
|
||
code.
|
||
#include <vector>
|
||
#include <algorithm>
|
||
|
||
int main() {
|
||
int a;
|
||
std::vector<std::vector<int>> v;
|
||
std::vector<std::vector<int>>::const_iterator it = std::find(v.begin(), v.end(), a);
|
||
}
|
||
a(href="https://tio.run/##jY1BDoIwEEX3PcUkbiBRDlCwVzF1OtZJoCVl7MZ49lJgoUv/6uXl/3yc54tHLOXEAceXIxgyocRk1NfY0cfE8pyMUhwEJsuhaeGtoGYTtt9xEaf1MR9@uVaMgfxHSWuMYZEbCyVbLbDA9Zg8OLgmd3fy2/kZckdVVLBtrz6lrA") Try it online!
|
||
p The error message (compiled with GCC 10.2.0) has 148 lines with about 12 kB of content, starting with:
|
||
pre
|
||
samp.
|
||
In file included from /usr/include/c++/10.2.0/bits/stl_algobase.h:71,
|
||
from /usr/include/c++/10.2.0/vector:60,
|
||
from error.cpp:1:
|
||
/usr/include/c++/10.2.0/bits/predefined_ops.h: In instantiation of ‘bool __gnu_cxx::__ops::_Iter_equals_val<lt;_Value>::operator()(_Iterator) [with _Iterator = __gnu_cxx::__normal_iterator<lt;std::vector<lt;int>*, std::vector<lt;std::vector<lt;int> > >; _Value = const int]’:
|
||
/usr/include/c++/10.2.0/bits/stl_algobase.h:1932:14: required from ‘_RandomAccessIterator std::__find_if(_RandomAccessIterator, _RandomAccessIterator, _Predicate, std::random_access_iterator_tag) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<lt;std::vector<lt;int>*, std::vector<lt;std::vector<lt;int> > >; _Predicate = __gnu_cxx::__ops::_Iter_equals_val<lt;const int>]’
|
||
/usr/include/c++/10.2.0/bits/stl_algobase.h:1977:23: required from ‘_Iterator std::__find_if(_Iterator, _Iterator, _Predicate) [with _Iterator = __gnu_cxx::__normal_iterator<lt;std::vector<lt;int>*, std::vector<lt;std::vector<lt;int> > >; _Predicate = __gnu_cxx::__ops::_Iter_equals_val<lt;const int>]’
|
||
/usr/include/c++/10.2.0/bits/stl_algo.h:3902:28: required from ‘_IIter std::find(_IIter, _IIter, const _Tp&) [with _IIter = __gnu_cxx::__normal_iterator<lt;std::vector<lt;int>*, std::vector<lt;std::vector<lt;int> > >; _Tp = int]’
|
||
error.cpp:7:87: required from here
|
||
/usr/include/c++/10.2.0/bits/predefined_ops.h:268:17: error: no match for ‘operator==’ (operand types are ‘std::vector<lt;int>’ and ‘const int’)
|
||
268 | { return *__it == _M_value; }
|
||
| ~~~~~~^~~~~~~~~~~
|
||
In file included from /usr/include/c++/10.2.0/bits/stl_algobase.h:67,
|
||
from /usr/include/c++/10.2.0/vector:60,
|
||
from error.cpp:1:
|
||
/usr/include/c++/10.2.0/bits/stl_iterator.h:1064:5: note: candidate: ‘template<lt;class _IteratorL, class _IteratorR, class _Container> bool __gnu_cxx::operator==(const __gnu_cxx::__normal_iterator<lt;_IteratorL, _Container>&, const __gnu_cxx::__normal_iterator<lt;_IteratorR, _Container>&)’
|
||
1064 | operator==(const __normal_iterator<lt;_IteratorL, _Container>& __lhs,
|
||
| ^~~~~~~~
|
||
/usr/include/c++/10.2.0/bits/stl_iterator.h:1064:5: note: template argument deduction/substitution failed:
|
||
li If you forget a semicolon…
|
||
p Credit goes to
|
||
a(href="https://codegolf.stackexchange.com/a/205268/98955") this StackExchange answer
|
||
| .
|
||
pre
|
||
code.
|
||
constexpr double pi = 3.0 // An engineer approximation
|
||
|
||
#include <iostream>
|
||
|
||
int main() {
|
||
std::cout << "Enter the radius: " << std::flush;
|
||
double radius;
|
||
std::cin >> radius;
|
||
std::cout << "Area: " << pi * radius * radius << std::endl;
|
||
}
|
||
a(href="https://tio.run/##ZY7NCsIwEITveYpBLypoBW9tLXjwQWKy6kK7CfkBQXz2WrXVg7dl@PabMd6vL8b0vXESE918gHX51BI8Y4/dZouiwEFAcmEhCtDeB3fjTid2otScxbTZEmp2MQXSXaMUS0KnWRZL3BUQky1L43JCXWN2lDRo0pUQtOUcS8xe@Rs6tzleq@FlHPEhqq@DBU3zl07mw1A/2ob1q5H7HVMLiW0r9ej7Jw") Try it online!
|
||
p The error message (compiled with GCC 10.2.0) has 3244 lines with about 250 kB of content, starting with:
|
||
pre
|
||
samp.
|
||
In file included from /usr/include/c++/10.2.0/iostream:38,
|
||
from error.cpp:3:
|
||
/usr/include/c++/10.2.0/x86_64-pc-linux-gnu/bits/c++config.h:258:1: error: expected ‘,’ or ‘;’ before ‘namespace’
|
||
258 | namespace std
|
||
| ^~~~~~~~~
|
||
In file included from /usr/include/c++/10.2.0/iosfwd:40,
|
||
from /usr/include/c++/10.2.0/ios:38,
|
||
from /usr/include/c++/10.2.0/ostream:38,
|
||
from /usr/include/c++/10.2.0/iostream:39,
|
||
from error.cpp:3:
|
||
/usr/include/c++/10.2.0/bits/postypes.h:98:11: error: ‘ptrdiff_t’ does not name a type
|
||
98 | typedef ptrdiff_t streamsize; // Signed integral type
|
||
| ^~~~~~~~~
|
||
/usr/include/c++/10.2.0/bits/postypes.h:41:1: note: ‘ptrdiff_t’ is defined in header ‘<cstddef>’; did you forget to ‘#include <cstddef>’?
|
||
40 | #include <cwchar> // For mbstate_t
|
||
+++ |+#include <cstddef>
|
||
41 |
|
||
In file included from /usr/include/c++/10.2.0/bits/exception_ptr.h:40,
|
||
from /usr/include/c++/10.2.0/exception:147,
|
||
from /usr/include/c++/10.2.0/ios:39,
|
||
from /usr/include/c++/10.2.0/ostream:38,
|
||
from /usr/include/c++/10.2.0/iostream:39,
|
||
from error.cpp:3:
|
||
/usr/include/c++/10.2.0/new:126:26: error: declaration of ‘operator new’ as non-function
|
||
126 | _GLIBCXX_NODISCARD void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)
|
||
| ^~~~~~~~
|
||
/usr/include/c++/10.2.0/new:126:44: error: ‘size_t’ is not a member of ‘std’; did you mean ‘size_t’?
|
||
126 | _GLIBCXX_NODISCARD void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)
|
||
| ^~~~~~
|
||
p I guess the fourth line does tell you that you missed a semicolon (if you have the patience to scroll to it), but it's not exactly clear where.
|
||
li Would you prefer an error that I actually encountered? Here is a simplified version of some code I wrote in a programming contest:
|
||
pre
|
||
code.
|
||
#include <algorithm>
|
||
#include <iostream>
|
||
#include <numeric>
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
std::string decrypt(std::string& k, std::string& m) {
|
||
std::vector<std::string> c(k.length());
|
||
int n = m.length() % k.length() == 0 ? m.length() / k.length() : m.length() / k.length() + 1;
|
||
int a = 0;
|
||
for (std::string& s : c) {
|
||
for (int i = 0; i < n; i++) {
|
||
s += a >= m.length() ? ' ' : m[a];
|
||
a++;
|
||
}
|
||
}
|
||
std::vector<int> j(k.length());
|
||
std::iota(j.begin(), j.end(), 0);
|
||
std::sort(j.begin(), j.end(), [&k](int x, int y) { return k[x] < k[y]; });
|
||
std::vector<int> q(k.length());
|
||
for (int i = 0; i < k.length(); i++) {
|
||
q[j[i]] = i;
|
||
}
|
||
std::string r = "";
|
||
for (int i = 0; i < n; i++) {
|
||
for (int ii = 0; ii < k.length(); ii++) {
|
||
char h = c[q[ii]][i];
|
||
if (c != ' ') {
|
||
r += c;
|
||
}
|
||
}
|
||
}
|
||
return r;
|
||
}
|
||
|
||
int main() {
|
||
std::string k;
|
||
std::getline(std::cin, k);
|
||
std::string m;
|
||
std::getline(std::cin, m);
|
||
std::cout << decrypt(k, m) << std::endl;
|
||
}
|
||
a(href="https://tio.run/##fZPbboMwDIbveQqv0zYQrOtuy6EPgrhgaQrhENoQplYTz87MqQldOyFE8P/F/LYDOR7fE0K67plxUjR7Cl5cJJVgMi0DQwVZVUtB40WMNyUVjOghhBhP9Mg3JbISgWHUcr/djjrsKRGXozS12CvkDizeSwt@DBhjYxZP0wMgZr4uKE9kalqWiyTjEjj4UF7D8AKKAd@HDex0@UOXtw8VGz7n/DHm3/Qvh0rA0n@NGcjoeZL7DWzYgA8POD5seyawMrB9TBgsHO/gDS/0EsaRO4GxbY/L1hhvvSf4kQCy214MBKtkbGbrL5owbloOZGvK9/1io5i6EvIuE77m0VDB2Rkqv6BxEFQ2gkMeniMsKA8vkQutSqZbOt1autcShSx6cwqzkEURcszVS56Oj0BhtXqUc9lmRczIn@8uhkLSWECKKAlPIUMTaGSeAzuASeDJ70ekdgD6wUGSmWoXo5o6JlyjNYzeRxn3jVZHe6opvzYxobJgnI6HizDuQK6Na6TLf@hS0aRqJHje9X/Le7UPDCoOukBbXfcL") Try it online!
|
||
p The error message does tell you where the error is, but you have to scroll through 228 lines with over 22 kB of content.
|