Supplement to this video. Watch first 35 min (the language bits), then 1:14:30 - 1:18:15 (the new map features).
You can find a fairly comprehensive list of new features on cppreference or here if you really want to see it all.
auto [promise, future] = makePromiseFuture<int>();
for (auto&& [key, val] : someMap) { /* ... */ }Automatically works with all-public structs, arrays, std::pair and
std::tuple. Other types may be supported if they supply a tuple-like
protocol (see docs for details).
if and switch with initializers
if (auto status = doThing(); !status.isOK())
return status;
if (auto num = parsNumberFromString<int>(str); !num.isOK()) {
return num.getStatus();
} else {
return doThing(num.getValue());
}
switch (auto res = inputDocSource->getNext(); res.getStatus()) {
case kEOF:
case kPauseExecution:
return res;
case kAdvanced:
return process(res.releaseDocument());
}template <typename RetType, typename F, typename... Args>
RetType call(F&& func, Args... args) {
if constexpr (std::is_same<RetType, void>()) {
func(args...);
} else {
return func(args...);
}
}Yes, the feature is called "constexpr if" but spelled if constexpr...
template <typename Nums>
auto sum(Nums... nums) {
return (0 + ... + nums);
}You will generally want to use "left folds" which are the forms which put the
... before the pack to be expanded.
auto ints = std::vector{1,2,3};
auto lk = std::unique_lock(someMutex);
// used to need std::unique_lock<std::mutex>(someMutex)
std::unique_ptr<SomeType> factory();
auto shared = std::shared_ptr(factory());
// Deduction guides let types customize the behavior:
template <typename T>
MyContainer(const std::vector<T>&) -> MyContainer<T>;
template <typename It>
MyContainer(It begin, It end) -> MyContainer<typename std::iterator_traits<It>::value_type>;template <auto CodeOrCategory>
using ExceptionFor = /* ... */;
try {
/*...*/
} catch(ExceptionFor<ErrorCodes::HostNotFound>& ex) {
/*...*/
} catch(ExceptionFor<ErrorCategory::NotMasterError>& ex) {
// Used to need ExceptionForCat<...> here.
}inline variables
class Classy {
inline static int count = 0;
// Used to need to define separately in a cpp file.
// static constexpr members are implicitly inline.
static constexpr int kNum = 42;
static constexpr StringData kName = "Bob"_sd;
};constexpr auto example = [] () constexpr {};
// constexpr on lambdas' call operators is automatic if it is legal.
constexpr auto frobnicate = [] (int i) { return i * 42; };
constexpr auto res = frobnicate(21);static_assert(some_condition());
// used to require a second string argument.
// We use MONGO_STATIC_ASSERT(cond) to get this now.auto lk = std::lock_guard(mutex);
UnmovableType func() {
return UnmovableType(args);
// This still isn't legal (no NRVO):
UnmoveableType out;
out.doThing();
return out;
}
auto res = func();Prefer using that style of declaration in C++17 over std::lock_guard lk(mx);
as it is more visually distinct from std::lock_guard(mx); and std::lock_guard mx;
so you are less likely to accidentally type them, and they will stand out more in
reviews.
namespace mongo::repl {
/*...*/
} // namespace mongo::repl// Replaces val if key already present:
auto [it, inserted] = myMap.insert_or_assign(key, val);
// Does nothing (not even construct the value) if key is present:
auto [it, inserted] = myMap.try_emplace(key, args_to_value_constructor...);
map<string, vector<int>>().try_emplace("") // value is empty vector.
map<string, vector<int>>().try_emplace("", 5) // value is vector with 5 zeros.
map<string, vector<int>>().try_emplace("", 5, 1) // value is vector with 5 ones.
map<string, pair<int, string>>().try_emplace("", 5, "one") // value is pair(5, "one").
// Extract node from map:
auto node = map.extract(keyOrIterator);
node.key().mutate(); // key is mutable when outside of any map.
otherMap.insert(std::move(node));
// Steals all nodes from source that don't have equivalent key in map:
map.merge(source);Docs:
insert_or_assign
try_emplace
extract
merge
std::string_view is basically mongo::StringData
optional, variant and any pulled from Boost to std::
boost::filesystem standardized as std::filesystem
Polymorphic Memory Resources (PMR) Allocators
Parallel STL algorithms and execution policies
C++17 added a lot of lib extensions, but I think only a few will be useful to most of us.
Finally a locale-independent and (hopefully) fast way to convert numbers to and
from strings. Explicitly designed for things like JSON parsing/serialization. It
has a somewhat complicated, low-level API to avoid requiring allocating or any
other potentially expensive operations. We may want to replace the
implementation of parseNumberFromString() with a call to from_chars().
invoke(f, args...)
performs the operation the standard has used the magic INVOKE token for, but
never provided a way to use. It calls f passing args..., but in addition to
normal functions and function-objects, it also supports pointer-to-members
(&type::member) and pointer-to-member-functions (&type::method). For them,
it behaves like the implicit this parameter added as an explicit first
parameter, and it supports passing both references and pointers to the object.
Also:
is_invocable<F, Args...>andinvocation_result_t<F, Args...>do what they look likeis_invocable_r<RetType, F, Args...>to test if the return type ofF(Args...)is compatible withRetType. Everything is compatible withvoid.
apply(f, tuple)is likeinvokebut splats the tuple into argumentsmake_from_tuple<T>(tuple)is likeapplybut constructs aTobject
Variadic lock guard called scoped_lock
Like a mix of lock_guard with
lock() to let you acquire
multiple locks while avoiding deadlocks. Not movable like unique_lock.
// Two functions with these lines are guaranteed *not* to deadlock:
auto lk = std::scoped_lock(mx1, mx2);
auto lk = std::scoped_lock(mx2, mx1);
// Can also be used just like plain old lock_guard:
auto lk = std::scoped_lock(mx);boyer_moore_searcherSub-linear needle-in-haystack string search algorithmclamp(val, min, max)returnsvalrestricted to betweenminandmaxshared_ptr<T[]>array supportweak_from_this()likeshared_from_this()as_const(val)returns a const-ref to val- Proposed by ADAM
In rough order most useful to least.
This is really useful, but hard to demo because it basically just makes code do what we expect when reading it.
Example from the standard:
std::string s = "but I have heard it works even if you don’t believe in it";
s.replace(0, 4, "")
.replace(s.find("even"), 4, "only")
.replace(s.find(" don’t"), 6, " ");
assert(s == "I have heard it works only if you believe in it"); // Now OKThis example was included in Bjarne Stroustrup's book The C++ Programming Language, 4th Edition, which was reviewed by several experts but nobody noticed this bug. It actually produces different output on different compilers.
These are the additional rules. All examples use letter order to show evaluation order. Anything indeterminately sequenced will use the same letters.
- Using an overloaded operator keeps the same order as builtins (
A, Beven if the comma operator is overloaded, but still don't do that!) - Function objects evaluated before arguments (
(A(B))(C)) - Function arguments are still evaluated in arbitrary order, but now at least
each argument is fully evaluated before moving on to the next (
A(C(B, B), D)orA(D(C, C), B), but notA(D(B, B), C)- This means that
f(unique_ptr<int>(new int()), may_throw())is no longer a potential leak. Item 17 from Effective C++ can now be forgotten!
- This means that
- Assignment evaluates RHS before LHS (
B = AandB += A)- This prevents leaving an unwanted element in code like
my_map[key] = may_throw()
- This prevents leaving an unwanted element in code like
- Object before member access, including methods and pointer-to-member usages
(
A.B(C),A->B(C),(A.*B)(C),(A->*B)(C))- This makes chaining/fluent APIs work as intended
- Container before subscripts (
A[B]) - Shifts (AKA stream-to and stream-from) are left to right (
A << B << CandA >> B >> CandA >> B << C)
You can find the full list of rules (old and new) here.
[this] { method()}; // Captures the object by pointer/reference
[self = *this] { self.method(); } // C++14 capture by copy
[*this] { method(); } // C++17 capture by copy
[self = std::move(*this)] { self.method(); } // Still only way to capture by moveIt behaves as-if the bases were initial members, recursively. This lets you avoid writing constructors for simple types.
struct Person { std::string name; };
struct Employee : Person { int id = 0; };
auto person = Person{"John Doe"}; // Valid in C++14
auto employee = Employee{"Bob Smith", 1234}; // Now also valid[[nodiscard]]- On functions, issues a warning if the return value is "unused". On types, acts as if all functions that return the type were marked[[nodiscard]]- Standardization of non-std attribute we already use on
Status,StatusWith, andFuture - The standard is intentionally vague about what "unused" means to allow compilers freedom to choose their own hueristics, and most seem to do a good job
- Casting-to-void is defined to suppress the warning (
(void)dont_ignore_me();)
[[nodiscard]] bool returnsFalseOnFailure(); struct [[nodiscard]] Status { /*...*/ }; Status doThing(); void func() { returnsFalseOnFailure(); // Warning! doThing(); // Warning! (void)doThing(); // no warning, result intentionally ignored. }
- Standardization of non-std attribute we already use on
[[fallthrough]]- Suppress warnings about implicit fallthrough in switch statementsswitch(var) { case A: do_a_stuff(); [[fallthrough]]; // no warning case B: do_a_and_b_stuff(); // forgot break; - warning on recent compilers case D: case C: // No warning if no code between cases. do_c_and_d_stuff(); }
[[maybe_unused]]- Suppress warnings about unused entities. Most useful for variables only used in some build modes.[[maybe_unused]] const int orig_size = obj.size(); // Add and remove items from obj... #ifdef DEBUG assert(obj.size() == orig_size); #endif
using MayThrow = void();
using NoThrow = void() noexcept;
void mayThrow();
void noThrow() noexcept;
MayThrow* a = mayThrow;
MayThrow* b = noThrow; // implicit conversion to weaker type.
NoThrow* d = noThrow;
NoThrow* c = mayThrow; // no longer compiles!
// Never enforced noexcept, but now won't compile.
// Maybe it can be made to actually work in the future?
std::function<void() noexcept> f;Removed trigraphs
"Finally??!" != "Finally|"
"Enter date ??/??/??" != R"(Enter date \\??)"Only practical use seems to be pulling in a method from all base types when using a template pack of bases. This makes it easy to build a type to overload lambdas.
using std::unique_ptr, std::shared_ptr; // meh
struct A { int operator()(int); };
struct B { double operator()(double); };
struct meh : A, B {
using A::operator(), B::operator(); // still meh
};
template <typename... T>
struct overloaded : T... {
using T::operator()...; // now we're cooking!
};
// class template deduction guide:
template <typename... T> overloaded(T...) -> overloaded<T...>;
overloaded<A, B> meh_again; // Overloaded function object
meh_again(1); // returns int
meh_again(1.0); // returns double
auto oh_yeah = overloaded{
[](int) {},
[](double) {},
};[[using gnu: cold, noinline]] inline cold_code_path() {}I really can't think of a reason to use this unless your source files are in EBCDIC. Only justification I can think of is for symmetry with u8-string literals from C++11.
// New in C++17
auto c1 = u8'c'; // Yup, its a char...
// Before
auto c2 = 'c'; // Also a char
auto c3 = u'c'; // char16_t
auto s1 = u8"s"; // Added in C++11