Unlike C++11, this is a minor release, focused mostly on improvements on top of
C++11 changes, with very little that one could call “new”. C++14 feels a little
more natural than C++11 by expanding the usage of features and implementing
common sense additions that were missed in the original C++11 release. There
were also quite a few bug fixes; several of these were backported into C++11
mode in compilers.
Also, while C++11 is always available in ROOT 6, C++14 requires a flag and
compatible compiler, so C++14 features are often unavailable. The Conda-Forge
ROOT package has C++17 enabled.
Auto and lambdas
Type syntax is more consistent in C++14, with auto working in more places. You
can now write a function returning auto or decltype(auto) and it will deduce
the return type from the return statements instead of having to use the peculiar
trailing return type syntax and lots of decltype work (N3638).1 This
should be used with caution when designing a function for a third party use; you
should always present a clear interface, and auto obscures that. However,
massive unreadable return types computed from the arguments could obscure it far
more. A related improvement with lambdas is that they now support auto for
parameters; this allows a template lambda functions to be created and reused
(called a generic lambda, N3649). This will end up being very powerful in
C++17 with variants, since the objects created truly are generic until
instantiated. They have also gained slightly more powerful capture abilities
(N3648); a capture can now name a new variable and give it an initializer,
which is how you move a value into a lambda:
auto data = std::make_unique<std::vector<double>>(1000);
auto job = [d = std::move(data)] { return d->size(); };
Constexpr
This expression has gained quite a bit in C++14 (N3652); you can now use if,
switch, and loops inside a constexpr, as well as variable definitions and
mutating local objects. A bit more of the standard library now includes
constexpr, as well, such as std::array (N3470), <chrono> (N3469), and
<utility>/<tuple> (N3471). Most of the algorithms still do not have
constexpr and thus require using a
third party library, sadly.
A related change is the addition of variable templates (N3651). You can now
define a variable with template specializations; for example, you could define
pi to provide a double and a string representation all in one variable,
depending on how it is used. This could also be used to define a constant
variable without being tied to a library.
template <typename T>
constexpr T pi = T(3.1415926535897932385L);
double circle_area(double r) { return pi<double> * r * r; }
Smaller changes
The standard library received a few improvements, as well. The C++ literals
syntax is finally supported by the standard library (N3642). You can now
write:
using namespace std::string_literals;
some_string_function("This is a std::string"s + " simply by adding an s at the end"s);
Other standard library types have literals support for units now too, such as
the chrono library. As an example where a duration is created:
using namespace std::literals::chrono_literals;
auto duration = 1h + 2min + 3s + 4ms + 5us + 6ns;
Complex numbers also gained a set of literals, as well (N3779). The literal
syntax is entirely a C++11 construct, the new feature is just the addition of
the predefined literals to the standard library (albeit in an opt-in namespace).
These literals, however, are free from the requirement that a user-defined
literal must follow; they do not start with an underscore.
Digit separators, using single quotes, can make large numbers more readable
(N3781). They are ignored by the compiler and are only visual aids. Binary
literals are now available, using a 0b prefix (N3472).
constexpr int n_events = 1'000'000; // easier to read than 1000000
constexpr int mask = 0b1010'1010;
An omission of the C++11 standard was fixed with the addition of
std::make_unique to mimic std::make_shared for unique smart pointers
(N3656).
The type system has received minor improvements, with _t type aliases to
reduce typing (pun intended, N3655), and a std::enable_if_t helper type
makes std::enable_if slightly less verbose (if you are stuck in C++11, this is
easy to define using the
possible implementation).
Two additions matter if you write generic code over tuples. std::get<T>() gets
a tuple element by type instead of by index (N3670), and
std::integer_sequence / std::index_sequence give you a compile-time list of
indices, which is the standard way to expand a tuple into a function call
(N3658):
template <typename F, typename Tuple, std::size_t... I>
auto apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>) {
return f(std::get<I>(std::forward<Tuple>(t))...);
}
That pattern became std::apply in C++17, but you had to write it by hand here.
Even smaller things
A few more items that are easy to miss:
- Transparent operator functors:
std::less<> deduces its argument types
(N3421), which enables heterogeneous lookup in associative containers
(N3657) — you can now find a std::string key with a const char* and
avoid the temporary. std::exchange: set a new value and return the old one, useful for move
assignment (N3668)[[deprecated]]: a standard attribute, with an optional message (N3760)- Aggregates can now have default member initializers and stay aggregates
(N3653)
std::shared_timed_mutex and std::shared_lock bring reader/writer locks to
the standard library (N3659, N3891)- Dual-range
std::equal, std::mismatch, and std::is_permutation take both
ends of the second range, so a length mismatch is no longer undefined behavior
(N3671) - Null forward iterators: value-initialized iterators compare equal (N3644)
- Sized deallocation gives
operator delete the object size, which some
allocators are much faster with (N3778) std::quoted for round-tripping strings with spaces through streams (N3654)
Bugfixes
Several bugs in C++11 were addressed, as well. Most compilers backport these
fixes into C++11 mode, such as GCC4.8/4.9. One example is type overloading with
std::function; in C++11, you could not distinguish between different
std::function signatures for function or method overloads, but in C++14 and
newer C++11 compilers you can.
Further reading
A few other resources:
AI Usage Disclaimer
This post was revised with AI
assistance: the code samples were compiled and checked, paper links were added,
and a few missing C++14 features were filled in.
If you use auto, auto&, or auto&&, you will explicitly define the
result’s value category, just like if you had used this in a variable
declaration. If you use decltype(auto), the value category will be deduced
– this is often what you want. ↩︎