Introduction
This small article contains 20 features since C++ 11 and up to C++ 17 that appealed to me, so I present them to you, along with some nasty comments. Happy reading! This article will be updated constantly.
Background
Yes, assume full C++ 11 knowledge. This is not a beginner's article.
The Features
New and Updated Literals
int a = 0b111;
auto str = "hello"s; auto dur = 60s; auto z = 1i;
+1, although I would somewhat prefer to not excessively use auto everywhere.
Digit Separators
int a = 1'000'000;
+1. Makes reading large numbers (especially 64 bit pointers) easier.
Variable Templates
template <typename T> T pi = (T)3.141592;
double x = sin(2.0*440.0*pi<double>*0.01); float perimeter = 2.0f*pi<float>*radius;
=0. I haven't really found anything useful for it yet. Templating a function or a class OK - you want to use it with numerous types. Templating a variable means that I have the same name for actually different variables - ouch. Actually, the only example I've found online is the pi version.
Function Return Auto Deduction
auto foo(int j,int x)
{
return j + x; }
auto boo();
auto tu() {
return make_tuple<int,int,int>(5,6,7);
}
int main()
{
auto t = tu(); int a = foo(5,6); int b = boo(); }
+1. Mostly useful for lengthy-types like the tuple above. If there are multiple return statements, all of them must deduce the same type. Since this is a single-pass, the deduction must be available when it is needed. For example, the int b = boo()
line will fail because boo
hasn't been defined yet, even if the line suggests an int
-value. Defining the function in another TU, or even in the same TU but below that code will fail.
Related Lambdas
auto lambda = [](auto x, auto y) {return x + y;};
+1. Auto enables you to type less.
Namespace Nesting
namespace A
{
namespace B
{
namespace C
{
}
}
}
namespace A::B::C
{
}
+1. Significantly less typing.
Structure Binding
tuple<int,float,char> x() { return make_tuple<int,float,char>(5,5.2f,'c'); }
int a;
float b;
char c;
std::tie(a,b,c) = x();
auto [a,b,c] = x();
+1. It also works in structures. The problem is that you can't use std::ignore
, like in the old tie.
std::optional
using namespace std;
optional<string> foo()
{
if (something)
return "Hello";
return {};
}
int main()
{
auto rv = foo();
if (rv)
{
}
rv.value_or("dada"); }
+1. Allows a "nothing" return value to be returned while providing a catchall case with value_or
. HOWEVER, this does not work as intended:
using namespace std;
optional<int> foo(int x)
{
if (x == 1)
return 5;
}
Now I still get a compiler warning, and probably an undefined-behaviour std::optional
. When there is a non returning path, return {}
should be implied (Yes, I've proposed it and yes, they all disagree with me. Who cares. :))
std::any
using namespace std;
any foo()
{
string h("hello");
return any(h);
}
int main()
{
try
{
auto r = any_cast<string>(foo());
}
catch(...)
{
}
}
+1 I do not like it much, but I have found a nice trick with it.
std::variant
union A
{
int a;
float b;
char c;
};
A x;
x.a = 5;
float b = x.b;
using namespace std;
variant<int,float,char> x;
x = 5; int i = std::get<int>(v); std::get<float>(v);
=0, std::variant
is not a normal union, but a type-safe union, because it throws when you try to access the non-current type. However, the point of the old-type unions is to share memory without types (consider the old x86 union ax
{ struct {char al,char ah}; short ax; }
). I'm not very sure of a useful case scenario.
Fold Expressions
template <typename T>
T add(T t) { return t; }
template <typename T, typename... Many>
T add(T t, Many... many) {
return t + add(many...);
}
template <typename T, typename ... Many>
T add(T t, Many... many)
{
return (t + ... + many);
}
int main()
{
auto r = add(1,2,3,4,5,6,7,8,9,10); }
+100. No comments. The idea is to combine the ellipsis with one of the operators to produce a non-comma expansion of the pack.
#if has_include
#if __has_include(<string>)
#else
#endif
=0. It's OK, generalizes old #pragma
once and #IFNDEF _STRING_H
stuff.
template <auto>
template <auto value> void foo() { }
foo<3>();
+1, autos anywhere. :)
Some New Attributes
void [[deprecated("This function is not safe")]] foo ()
{
}
switch(a)
{
case 1:
test(); case 2:
test2();
[[fallthrough]]; case 3:
}
[[nodiscard]] int foo()
{
return 1;
}
int main()
{
foo(); int a = foo(); }
int foo()
{
[[maybe_unused]] int y;
}
+1, they are nice standarizations for older pragma's.
if and switch init Before Conditions
int val = foo();
if (val == 1)
{
}
if (int val = foo() ; val == 1)
{
}
+1, the difference is that the variable's scope is inside the if
only.
Inline Variables
inline int x = 5;
+1000, now you can include also variables in .h files without problem. It can be included as many times as needed and it's only defined once.
Auto Deduction from Initializer Lists
std::initializer_list<int> x1 = { 1, 2 };
auto x1 = { 1, 2 };
+1. Nice auto again.
Constructor Template Deduction
auto p = std::pair<double,int>(5.0,0);
auto p = std::pair(5.0,0);
+1000, it was about time.
Exception Specifications are Part of the Type
void (*p)() throw(int); void (**pp)() throw() = &p;
+1. In C++ 17, the above is an error. The exception specification throw(int)
is now part of the type.
And finally.....
string_view
using namespace std;
string a = "hello there";
string_view largeStringView{a.c_str(), a.size()};
+1000. A string_view
gives us all the benefits of std::string
but on a string
that is already owned (either be another string
or a char
array). Therefore, it can be used in non-mutable situations (like comparisons or substrings).
Acknowledgments
History
- 3rd January, 2018 : First release, happy new year!