JF Bastien <@jfbastien>
Slides borrow heavily from Bryce Adelstein Lelbach <@blelbach>
(If everything goes well)
See the cppreference table, or the individual status for:
if constexpr
constexpr
lambdas
template <auto>
noexcept
added to the type system
if (init; cond)
and switch (init; cond)
namespace A::B::C {}
static_assert()
__has_include()
<filesystem>
<memory_resource>
string_view
optional<>
variant<>
any
new
shared_mutex
and variadic lock_guard<>
invoke()
, apply()
, and is_callable<>
*_v<>
variable templates for type traits
conjunction<>
, disjunction<>
, and negation<>
New bugs? 🐜
Do you do this often?
void g(T t) {
auto x = t.x;
auto y = t.y;
auto z = t.z;
// ...
}
How about this?
void g(T& t) {
auto& x = t.x;
auto& y = t.y;
auto& z = t.z;
// ...
}
Ain't this beautiful?
std::tuple<T1, T2, T3> f();
T1 x; T2 y; T3 z;
std::tie(x, y, z) = f();
Oops-prone...
std::tuple<T1, T2, T3> f();
int x; T2 y; T3 z;
std::tie(y, y, z) = f();
What about:
const
std::array<T, 3> f();
T x, y, z;
std::tie(x, y, z) = f(); // INVALID.
😕
struct S { T1 x; T2 y; T3 z; };
S f();
T1 x; T2 y; T3 z;
std::tie(x, y, z) = f(); // INVALID.
🤔
auto [x, y, z] = obj;
😁
The type of obj
must be Destructurable:
obj.get<>()
method or an ADL-able get<>(obj)
overload
std::tuple_size<>
and std::tuple_element<>
Why won't the compiler just figure it out for me?
std::tuple<int, double> t(-42, 3.14);
return std::tuple<int, double>(-42, 3.14);
auto t = std::make_tuple(-42, 3.14);
return std::make_tuple(-42, 3.14);
Any why make_*
all the things?
if constexpr
Template code is often specialized for the zero- and one-parameter case.
void g() { /* Zero parameters. */ }
template<typename T>
void g(T&& t) { /* Handle one T. */ }
template<typename T, typename... Rest>
void g(T&& t, Rest&&... r) {
g(std::forward<>(t)); // One T.
g(std::forward<Rest>(r)...); // And then the Rest, recursively.
}
if constexpr (cond1)
statement1;
else if constexpr (cond2)
statement2;
else
statement3;
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... a);
template<typename T, typename... Args>
std::enable_if_t<
std::is_constructible_v<T, Args...>, std::unique_ptr<T>
>
make_unique(Args&&... a) {
return std::unique_ptr(new T(std::forward<Args>(a)...));
}
template<typename T, typename... Args>
std::enable_if_t<
!std::is_constructible_v<T, Args...>, std::unique_ptr<T>
>
make_unique(Args&&... a) {
return std::unique_ptr(new T{std::forward<Args>(a)...});
}
😵
Similar to if constexpr
, template code often repeats what to do, or is expressed in terms of recursion.
auto sum() { return 0; }
template <typename T>
auto sum(T&& t) { return t; }
template <typename T, typename... Rest>
auto sum(T&& t, Rest&&... r) {
return t + sum(std::forward<Rest>(r)...);
}
Unary Right | (E op ...) | ≣ | E1 op (... op (EN-1 op EN)) |
Unary Left | (... op E) | ≣ | ((E1 op E2) op ...) op EN |
Binary Right | (E op ... op I) | ≣ | E1 op (... op (EN-1 op (EN op I))) |
Binary Left | (I op ... op E) | ≣ | (((I op E1) op E2) op ...) op EN |
== != < > <= >= && || , .* ->* =
+ - * / % ^ & | << >>
+= -= *= /= %= ^= &= |= <<= >>=
A gentleperson-programmer is someone who knows this fact but chooses not to exercise it. Especially when combined with operator overloading.
If the parameter pack is empty then the value of the fold is:
&& | ≣ | true |
|| | ≣ | false |
, | ≣ | void() |
For any operator not listed above, an unary fold expression with an empty parameter pack is ill-formed.
template <auto>
template <typename T, T v>
struct integral_constant {
static constexpr T value = v;
};
void f() { }
auto w = integral_constant<int, 2048>;
auto x = integral_constant<char, 'a'>;
auto y = integral_constant<bool, true>;
auto z = integral_constant<decltype(f), f>;
🙄 so much repetition
template <typename T, T... Elements>
struct integer_sequence { };
auto seq0 = integer_sequence<std::size_t, 0, 1, 2>;
auto seq1 = integer_sequence<char, 'h', 'i'>;
🙄 so much repetition
if (init; cond)
and switch (init; cond)
<filesystem>
namespace fs = std::filesystem;
boost.filesystem
fs::path
fs::directory_entry
fs::directory_iterator
fs::file_status
Exception versus error-code: why not both? 🤷
void copy(fs::path const& from, fs::path& to);
void copy(fs::path const& from, fs::path& to, std::error_code& ec);
std::string_view
A better const char*
std::string
API
std::optional<>
std::variant<>
Many STL algorithms, with parallel / vector
std::for_each(std::par, first, last,
[](auto& x){ process(x); }
);
std::copy(std::par, first, last);
std::sort(std::par, first, last);
std::transform(std::par_unseq, xfirst, xlast, yfirst,
[=](double xi, double yi){ return a * xi + yi; }
);
adjacent_difference is_heap_until replace_copy_if
adjacent_find is_partitioned replace_if
all_of is_sorted reverse
any_of is_sorted_until reverse_copy
copy lexicographical_compare rotate
copy_if max_element rotate_copy
copy_n merge search
count min_element search_n
count_if minmax_element set_difference
equal mismatch set_intersection
fill move set_symmetric_difference
fill_n none_of set_union
find nth_element sort
find_end partial_sort stable_partition
find_first_of partial_sort_copy stable_sort
find_if partition swap_ranges
find_if_not partition_copy transform
generate remove uninitialized_copy
generate_n remove_copy uninitialized_copy_n
includes remove_copy_if uninitialized_fill
inner_product remove_if uninitialized_fill_n
inplace_merge replace unique
is_heap replace_copy unique_copy
std::seq | indeterminately sequenced in the calling thread |
std::par | indeterminately sequenced with respect to each other within the same thread |
std::par_unseq | unsequenced with respect to each other and possibly interleaved |
See Hartmut Kaiser's CppCon talk!(slides)
He dives deep into C++ parallel algorithms, their performance, and where he thinks they'll go. Especially cool: parallel algorithms combined with auto
-lambda, SIMD, executors, and coroutines.
mind == 💥💥💥
Here's what we covered
if constexpr
constexpr
lambdas
template <auto>
noexcept
added to the type system
if (init; cond)
and switch (init; cond)
namespace A::B::C {}
static_assert()
__has_include()
<filesystem>
<memory_resource>
string_view
optional<>
variant<>
any
new
shared_mutex
and variadic lock_guard<>
invoke()
, apply()
, and is_callable<>
*_v<>
variable templates for type traits
conjunction<>
, disjunction<>
, and negation<>
[[fallthrough]]
, [[nodiscard]]
, and [[maybe_unused]]
for
*this
by value
auto a{b};
will not initialize initializer_list<>
operator++(bool)
, and register
destroy_*()
and new uninitialized_*()
utilities
.is_always_lockfree()
hardware_*_interference_size
uncaught_exceptions()
unique_ptr<>
owner_less<:void>
noexcept
cleanup for allocators and containers
as_const()
, clamp()
, sample()
, search()
, and searchers
const string::data()
vector<>
and lists
size()
, empty()
, and data()
void_t<>
and bool_constant<>
floor()
, ceil()
, round()
, and abs()
for <chrono>
types
auto_ptr<>
and random_shuffle()
Now compiling in toolchains near you!
These slides: