Question:
Translation based on Yakk's question from the OS in English .
All the features of C ++ 17 have already been approved, so it is unlikely to see any major changes. Hundreds of proposals were made for C ++ 17.
Which of these features were added to C ++ in C ++ 17?
Answer:
Language characteristics:
Templates and Generic Code
Deduction of template argument for template classes.
Technical document p0091r2
.
Suppose we have the following definitions:
std::vector<int> vi1 = { 0, 1, 1, 2, 3, 5, 8 };
std::vector<int> vi2;
std::mutex m;
unique_lock<std::mutex> ul(m, std::defer_lock);
template<class Func> class Foo() {
public:
Foo(Func f) : func(f) {}
void operator()(int i) {
os << "Calling with " << i << std::endl;
f(i);
}
private:
Func func;
std::mutex mtx;
};
Before the C ++ 17 standard, the following objects must be constructed as shown:
pair<int, double> p(2, 4.5);
auto t = make_tuple(4, 3, 2.5);
copy_n(vi1, 3, back_inserter(vi2));
// Virtualmente imposible pasar una lambda al constructor
// de una clase plantilla sin declarar la lambda
for_each(vi2.begin(), vi2.end(), Foo<???>([&](int i) { ...}));
lock_guard<std::mutex> lck(foo.mtx);
// Notacion del documento tecnico N4470
lock_guard<std::mutex, std::unique_lock<std::mutex>> lck2(foo.mtx, ul);
auto hasher = [](X const & x) -> size_t { /* ... */ };
unordered_map<X, int, decltype(hasher)> ximap(10, hasher);
If we allow the compiler to deduce the arguments for the template class constructors, we can replace the above code with:
pair p(2, 4.5);
tuple t(4, 3, 2.5);
copy_n(vi1, 3, back_insert_iterator(vi2));
// Ahora esto es facil, en lugar de virtualmente impossible
for_each(vi.begin(), vi.end(), Foo([&](int i) { ...}));
auto lck = lock_guard(foo.mtx);
lock_guard lck2(foo.mtx, ul);
// NOTA: la deducción de argumentos de plantilla deduce
// los argumentos no explícitos
unordered_map<X, int> ximap(10, [](X const & x) -> size_t { /* ... */ });
Declare non-type template arguments with auto
.
Technical document p0127r1
.
The type of a non-type template parameter must be explicitly specified, this causes unnecessary verbosity and reduces the flexibility of those templates that are intended to receive constant arguments of any type:
template <typename T, T v> struct S { }; // Definicion
S<decltype(x), x> s; // Instanciacion
The example uses decltype
to get the type of x
(a compile-time constant) before passing both parameters to S
Ideally, you should modify the declaration of S
so that the type of x
not required:
S<x> s; // Instanciacion deseada
This is achieved by allowing the use of auto
in the list of template parameters:
template <auto v> struct S; // Deduce el tipo de v
Allow constant evaluation of all nontype template arguments.
Technical document n4198
.
Before C ++ 17, C ++ allowed the following nontype template parameters:
- Any Integral or Enumerated Type that is a constant expression.
- Pointers that point to objects in static memory, bounding functions, or constant expressions that evaluate to a null pointer.
- References to objects in static memory or functions with binding.
- Pointers to members
&X::y
or constant expressions that evaluate to a null-member-pointer.
As of C ++ 17 the nontyping template parameters can be:
- Any Integral or Enumerated Type that is a constant expression.
- Pointers that point to an entire object in static memory, a function, or a null pointer.
- References to a glvalue referencing an entire object in static memory or a function.
- Any constant pointer-to-member expression.
-
std::nullptr_t
.
Allow typename
in template-parameters (template template)
Technical document n4051
It is mandatory to use class
for the type of a template-parameter:
template<template<typename> class X> struct S; // class es requerido
As of C ++ 17 you will also be able to:
template<template<typename> typename X> struct S; // Se puede usar class o typename
Folding expressions 1 .
Technical document n4295
.
A folding expression folds a template parameter package onto a folding binary operator.
The folding operators are:
+ - * / % ^ & | ~ = < > << >> += -= *= /= %= ^= &= |= <<= >>= == != <= >= && || , .* ->*
- An expression of the type
(... operador expresión)
whereop
is a folding operator is known as a left unary fold. - An expression of the type
(expresión operador ...)
whereop
is a folding operator is known as a right unary fold.
The left and right unary folds are known as unary folds. In a unary fold, the expression must contain an unexpanded parameter package.
- An expression of the type
(expresión1 operador1 ... operador2 expresión2)
in whichoperador1
andoperador2
are folding operators is known as a binary fold.
In a binary fold, operador1
and operador2
must be the same folding operator and expresión1
or expresión2
must contain a package of unexpanded parameters but not both. If expresión2
contains an unexpanded parameter package, the expression is known as a binary left fold. If expresión1
contains an unexpanded parameter package, the expression is known as a binary right fold.
Instantiating a collapsible expression produces:
-
((Expr1 operador Expr2) operador ...) operador ExprN
for left unary folds. -
Expr1 operador (... operador (ExprN-1 operador ExprN))
for right unary folds. -
(((Expr operador Expr1) operador Expr2) operador ...) operador ExprN
for binary left folds. -
Expr1 operador (... operador (ExprN-1 operador (ExprN operador Expr)))
for binary right folds.
In each case:
-
operador
is the folding operator -
N
is the number of elements in the expansion of the parameter package. -
ExprX
is generated by instantiating the pattern and replacing each parameter of the package with the X-th element.
For a binary folding expression, Expr
is generated by instantiating the expression that does not contain an unexpanded parameter package:
template<typename... Args>
bool todos(Args... args) { return (args && ...); }
bool b = todos(true, true, true, false);
When we instantiate the template todos
would todos
obtain:
bool todos(bool e1, bool e2, bool e3, bool e4) { return ((e1 && e2) && e3) && e4; }
New deduction rules for auto
with lists between keys.
Technical document n3922
In C ++ 17 the rules for deduction of types ( auto
) change for deductions involving lists of elements.
For list copy initialization:
-
auto
type deduction deductsstd::initializer_list
if the types in the list are identical or else it will be a compilation error.
For list initialization:
- In lists with only one element,
auto
will deduce the type of that element; prior to C ++ 17 you deduced astd::initializer_list
from an element. - In lists with more than one element,
auto
will not deduce, it will be a compilation error.
Example:
auto a = { 1, 2 }; // deduce std::initializer_list<int> de dos elementos
auto b = { 3 }; // deduce std::initializer_list<int> de un elemento
auto c{ 3 }; // deduce int, antes deducia std::initializer_list<int> de un elemento
if
constant.
Technical document p0128r1
.
An if
whose condition is evaluated at compile time and the untaken branch is discarded from compilation, is useful for simplifying certain codes:
void funcion()
{
// Se requiere la funcion vacia para eliminar la recursion
}
template <class T>
void funcion(T&& elemento)
{
// Hacer cosas con el elemento
}
template <class T, class... Parametros>
void funcion(T&& cabeza, Parametros&&... cola)
{
funcion(cabeza);
funcion(cola...); // Finaliza la recursion cuando cola... es una lista vacia
}
With if
constant, the empty function is not necessary:
template <class T>
void funcion(T&& elemento)
{
// Hacer cosas con el elemento
}
template <class T, class... Parametros>
void funcion(T&& cabeza, Parametros&&... cola)
{
funcion(cabeza);
constexpr if (sizeof...(cola))
{
// No se genera esta parte del codigo si cola... es una lista vacia
funcion(cola...);
}
}
Lambdas
Lambdas constant expression
Technical document n4487
.
Before C ++ 17 it was forbidden for a lambda or its instance (closure) to be part of a constant expression; This restriction was inconsistent (since traditional functors do not have it) and unnecessary:
// Este codigo es valido en C++17 pero incorrecto en estandares previos
constexpr auto L = [](int i) { return i; }; // Correcto en C++17
auto L2 = [] { return 0; };
constexpr int I = L2(); // Correcto en C++17
Capture *this
in lambdas
Technical document p0018r3
.
Lambdas declared in a non-static member function capture this
implicitly or explicitly to access the object's variables through the this
pointer; for this reason the capture of the object context by copy or by reference are the same:
struct S {
int x ;
void f() {
// Ambas lambdas son identicas pese a que su captura sea diferente:
auto a = [&]() { x = 42 ; } // El acceso a x se transforma en (*this).x = 42
auto b = [=]() { x = 43 ; } // El acceso a x se transforma en (*this).x = 43
}
};
This causes problems in asynchronous programming as it can cause the use of memory pointers that is no longer valid:
class Tarea {
private:
int valor ;
public:
Tarea() : valor(42) {}
std::future generar()
{ return std::async( [=]()->int{ return valor ; }); }
};
std::future foo()
{
Tarea tmp ;
// La closure asociada con el std::future devuelto
// tiene un puntero this implicito.
return tmp.generar();
// El puntero this de la closure deja de ser valido
// al finalizar la funcion, despues del return
}
int main()
{
std::future f = foo();
f.wait();
// El valor del futuro no es valido porque
// el objeto que lo genero ya ha sido destruido
assert( 42 == f.get() );
return 0 ;
}
Being able to capture *this
solves the problem.
Attributes
Attributes were added in C ++ 11 and C ++ 17 adds new attributes.
Attributes [[fallthrough]]
, [[nodiscard]]
and [[unused]]
Technical document p0068r0
.
The [[fallthrough]]
attribute is used as an instruction, the [[fallthrough]]
instruction is put just before a case
tag on a switch
and its goal is to mark that execution will fall to the next case
tag intentionally:
switch (c) {
case 'a':
f(); // WARNING: falta break
case 'b':
g();
[[fallthrough]]; // Correcto
case 'c':
h();
}
The [[nodiscard]]
attribute can be used on types or on functions; If it has been marked like this and the type or function return is not used in the code, a compilation alarm will be generated:
[[nodiscard]] int funcion();
void metodo() {
funcion(); // WARNING: valor de retorno de una funcion nodiscard es descartado.
}
[[nodiscard]] struct S { ... };
S crear_S();
void funcion() {
crear_S(); // WARNING: valor de retorno de un type nodiscard es descartado.
}
The [[unused]]
attribute marks an entity that for some reason is not used. If the compiler were to mark that entity with an alarm, it will not do so if the attribute is used:
// Una de las dos funciones no se usa dependiendo de UNA_CONDICION
static void funcion1() { ... }
static void funcion2() { ... } // warning: funcion2 no se usa
void iface() {
#ifdef UNA_CONDICION
funcion1();
#else
funcion2();
#endif
}
This attribute does not prevent the compiler from compiling the marked entity, so both functions will be compiled but neither will be marked with an alarm.
[[unused]] static void funcion1() { ... }
[[unused]] static void funcion2() { ... }
Attributes in Enumerated and Namespaces
Technical document n4266
.
Starting with C ++ 17, it is allowed to mark namespaces and enumerations with attributes.
Syntax
inline
variables.
Technical document n4424
.
C ++ requires that extern
functions and variables have exactly one definition in the program, this requirement was relaxed for functions by the inline
qualifier. Inline functions can be defined at various points, but all their definitions must be the same; the same is true for functions and variables instantiated from templates. Before C ++ 17 there was nothing similar for non-template variables although it is not strange to need a global and unique object without it having to be defined in a single translation unit.
Defining nested namespaces
Technical document n4230
.
As of C ++ 17 this code:
namespace A::B::C // Error en estandares previos a C++17
{
// codigo
}
It will be equivalent to:
namespace A
{
namespace B
{
namespace C
{
// codigo
}
}
}
Extend static_assert
.
Technical document n3928
.
As of C ++ 17 the second parameter of static_assert
is optional:
static_assert ( true != true ); // Correcto en C++17, incorrecto en estandares anteriores
static_assert ( false != false , "Falso debe ser falso" );
Clearer multiple return and flow control.
Structured Links 1
Technical document p0217r2
.
C ++ 17 offers the ability to declare multiple initialized variables from a tuple or structure:
std::tuple<T1,T2,T3> crear_tupla(/*...*/) { /*...*/ return { a, b, c }; }
auto [ x, y, z ] = crear_tupla(); // x deduce tipo T1, y deduce tipo T2, z deduce tipo T3
// iterador deduce tipo typeof(mapa)::iterator, insertado deduce bool
const auto [iterador, insertado] = mapa.insert({"clave", valor});
struct S { int x; volatile double y; };
S f();
// x deduce const int, y deduce const volatile double
const auto [ x, y ] = f();
Initializer in if
.
Technical document p0305r0
.
C ++ allows you to declare and initialize a variable in the if
, if this variable is evaluable as a Boolean condition, it will enter the true branch of the statement; but this is not possible when the variable is not evaluable as boolean:
if (int no_multiplo_de_3 = x % 3)
{
std::cout << no_multiplo_de_3 << " no es multiplo de 3\n";
}
// Error! decltype(mapa)::iterator no es evaluable como condicion
if (auto iterador = mapa.find("clave"))
{ ... }
C ++ 17 allows you to separate the declaration of the variable and its evaluation in the if
:
if (auto iterador = mapa.find("clave"); iterador != mapa.end())
{
std::cout << "clave existe en el mapa\n";
}
// Combinado con enlazados estructurados
if (const auto [iterador, insertado] = mapa.insert({clave, valor}); insertado)
{
std::cout << clave << " ha sido insertada en el mapa\n";
}
Range for
loop generalization.
Technical document p0184r0
.
Allows the type of the begin
iterator to be different from the type of the end
iterator.
Miscellany
Hexadecimal literals of floating-point numbers
Technical document p0245r1
.
There is a base-2 scientific notation for hexadecimal base floating-point numeric values: 0x3.ABCp-10. The mantissa is expressed in hexadecimal and the exponent in interpreted decimal with respect to base 2. The smallest IEEE-754 standard expressible simple precision value would be 0x1.0p-126
.
Dynamic memory allocation for aligned data.
Technical document p0035r1
.
C ++ 11 added the ability to specify alignment for objects, but did not specify any mechanism to properly allocate dynamic memory to these aligned objects, in C ++ 17 overloads are added to the new
and delete
operators to specify alignment:
void* operator new(std::size_t size, std::align_val_t alignment);
void operator delete[](void* ptr, std::align_val_t alignment) noexcept;
void operator delete[](void* ptr, std::align_val_t alignment, std::size_t size) noexcept;
Elision of guaranteed copy.
Technical document p0135r0
.
In C ++ 17 it is guaranteed that certain copies will be elided, in particular: when the object to be elided is a temporary value.
Refinement of the evaluation order of expressions for idiomatic C ++.
Technical document p0145r2
.
Progress guarantees for the Parallelism V2 technical specification.
Technical document p0299r0
.
UTF8 character literals.
Technical document n4267
.
C ++ 11 added the UTF text string literals:
auto utf8 = u8"UTF8"; // Literal de cadena de texto codificado en UTF8
auto utf16 = u"UTF16"; // Literal de cadena de texto codificado en UTF16
auto utf32 = U"UTF32"; // Literal de cadena de texto codificado en UTF32
C ++ 17 adds the UTF text character literals:
auto utf8 = u8'8'; // Literal de caracter codificado en UTF8
auto utf16 = u'F'; // Literal de caracter codificado en UTF16
auto utf32 = U'X'; // Literal de caracter codificado en UTF32
Specifying exceptions as part of the type of the function.
Technical document p0012r1
.
Specifying exceptions is not part of the function's type in pre-C ++ 17 standards:
void f_throw() throw(int) {}
void f_no_throw() {}
using void_throw = void() throw(int);
// Presuntamente un puntero a funcion void que puede lanzar excepciones
void_throw *p;
p = f_throw; // Correcto
p = f_no_throw; // Correcto tambien!
As of C ++ 17 an error would be generated:
p = f_throw; // Correcto
p = f_no_throw; // ERROR: No se puede convertir void() a void() throw(int)
Static verification of headers.
Technical document p0061r1
.
The __has_include
instruction __has_include
allow you to check if a header is available
#if __has_include(<optional>)
# include <optional>
# define EXISTE_OPTIONAL 1
#elif __has_include(<experimental/optional>)
# include <experimental/optional>
# define EXISTE_OPTIONAL 1
# define EXPERIMENTAL_OPTIONAL 1
#else
# define EXISTE_OPTIONAL 0
#endif
Fixes on conversions from arrays to pointers.
Technical document n4261
.
Fixes on legacy constructors.
Technical document p0136r1
.
What's new in STL
Type of data
std::variant
Secure-typed union.
Technical document p0088r2
.
std::optional
Object that may or may not contain an arbitrary value.
Technical document n4480
.
std::any
Object that can contain different types.
Technical document n4480
.
std::string_view
Reference to text strings.
Technical document n4480
.
std::shared_mutex
shared mutex.
Technical document n4480
.
Invoke
std::invoke
Generic call object.
Technical document n4169
.
You can call any callable object: function pointer, function, member function … with a unified syntax.
std::apply
Calls a function with a parameterized tuple.
Technical document n4480
.
File System Technical Specification v1
Technical document p0218r0
.
-
[class.path]
-
[class.filesystem.error]
-
[class.file_status]
-
[class.directory_entry]
-
[class.directory_iterator]
and[class.recursive_directory_iterator]
-
[fs.ops.funcs]
Parallelism Technical Specification v1
Technical document n4071
.
Library Fundamentals Technical Specification v1
Technical document p0220r1
.
-
[func.searchers]
and[alg.search]
. -
[pmr]
. -
std::sample
.