Características de C ++ 17 de un vistazo

Introducción

El segundo artículo de esta serie. Pero, de hecho, no quiero escribir el tercer artículo, que es para describir las características de C ++ 20. En este momento, la última versión de GCC es la 10.2. Después de la instalación, descubrí que todavía hay No hay forma de compilar C ++ 20. Puede que sea mi método No del todo correcto, pero de hecho es muy molesto. ¿No puedes escribir código para introducir lo que hace? Además, de todos modos, las grandes empresas no lanzarán C ++ 20 en los próximos cinco o seis años.

Volviendo a C ++ 17, esta actualización es mucho más grande que C ++ 14. Muchas de estas cosas pueden optimizar la legibilidad de nuestro código C ++ al tiempo que mejoran la eficiencia. Echemos un vistazo.
Inserte la descripción de la imagen aquí

Deducción de plantilla de constructor

De hecho, prefiero llamarlo deducción automática de tipo de plantilla. La visualización del código probablemente sea así:

 vector items = {
    
    1,2,3};
 // pair pa(4, "string"); 不直接匹配字符串
 pair pa(4, string("hihi"));
 cout << pa.second << " " << items[2] << endl;
 输出:
 hihi 3

Enlace estructurado

Puede vincular pares, tuplas, matrices y estructuras. Después del enlace estructurado, también puede modificar el valor original, o puede hacer que el tipo personalizado admita el enlace estructurado, pero no parece necesario modificar el std.

std::tuple<int, double> func_two() {
    
    
    return std::tuple(1, 2.2);
}

auto[i, d] = func_two();
cout << i << " " << d << endl;

map<int, string> mp = {
    
    
  {
    
    0, "a"},
  {
    
    1, "b"},
};

for(const auto& [x, y] : mp){
    
    
  cout << x << " " << y << endl;
}

pair pa(4, string("hihi"));
auto&[x, y] = pa;
cout << x << " " << y << endl;

输出:
1 2.2
0 a
1 b
4 hihi

/*       vector vec = {1,2,3};
      auto&[xx, yy, zz] = vec;
      cout << xx << endl; */ vector当然不能使用结构化绑定啦

inicialización de la instrucción if-switch

El alcance de las variables es más obvio.

if (int a = 29; a < 101) {
    
    
    cout << a;
}

Variable en línea

Esto es algo muy interesante. Puede pensar que la función en línea puede mejorar la eficiencia. ¿Cuál es el uso de la variable en línea? En realidad, esto implica una característica de inline que generalmente no es ampliamente conocida, es decir, el enlazador de C ++ selecciona solo una de las declaraciones repetidas en múltiples unidades de traducción, lo que significa que hay funciones y variables declaradas en línea que podemos declarar en el encabezado expediente.

El código tiene este aspecto:

// header file
struct A {
    
    
    static const int value;  
};
inline int const A::value = 10;

// ==========或者========
struct A {
    
    
    inline static const int value = 10;
}

Los amigos que estén interesados ​​pueden leer los dos artículos siguientes:

" Variable en línea de C ++ c ++ 17 "
" GCC, Clang en modo C, nivel de optimización más bajo, el enlazador informa errores indefinidos para las funciones en línea, ¿por qué?

Doble expresión

template <typename ... TT> 
constexpr auto foldSumRec(T... arg) {
    
    
    return (arg + ...);
}

C ++ 17 tenía que escribirse así antes:

template<typename T>
auto foldSumRec (T arg) 
{
    
    
    return arg;
}

template<typename T1, typename... Ts>
auto foldSumRec (T1 arg1, Ts... otherArgs) 
{
    
    
    return arg1 + foldSumRec(otherArgs...);
}

Para obtener detalles específicos, consulte este artículo " Expresión de 17 veces en C ++ "

constexpr expresión lambda

Cabe señalar que existen las siguientes restricciones: el cuerpo de la función no puede contener sentencias de ensamblaje, sentencias goto, etiquetas, bloques de prueba, variables estáticas, almacenamiento local de subprocesos, variables ordinarias sin inicialización, asignación dinámica de memoria, ninguna eliminación nueva, etc. y sin función virtual

 constexpr auto lamb = [](auto b) {
    
    
     int ret = 0;  // C++14中放宽了lambda的标准
     for (size_t i = 0; i < b; i++){
    
    
         ret += i;
     }
     return ret;
 };

anidación de espacio de nombres

namespace A {
    
    
    namespace B {
    
    
        namespace C {
    
    
            void func();
        }
    }
}

// c++17
namespace A::B::C {
    
    
    void func();
}

Azúcar sintáctico, más conveniente y cómodo.

función from_chars y to_chars

Para obtener más información, consulte " Función de conversión de cadenas y tipos numéricos incorporada de la biblioteca estándar de C ++ "

std::array<char, 3> str{
    
    "42"};
int result;
std::from_chars( str.data(), str.data()+str.size(),result );
std::cout << result << std::endl;

// p是填充到str以后的最后一个迭代器
if(auto [p, ec] = std::to_chars(str.data(), str.data() + str.size(), 425);
   ec == std::errc()){
    
    
    if(p == str.end()){
    
    
        std::cout << "hello world\n";
    }
        std::cout << std::string_view(str.data(), p - str.data());
}
输出:
42
hello world
425

std :: shared_mutex

Mil esperanzas y esperanzas de que finalmente estés aquí. . Esto no necesita explicarse, es el bloqueo de lectura-escritura.

Para la comparación de rendimiento con mutex, puede ver aquí "Comparación de rendimiento de std :: shared_mutex y std :: mutex (benchmark) "

std :: variante

Para obtener más información, consulte " C ++ 17 std :: variant "

struct NoDefConstr_seven{
    
    
    NoDefConstr_seven(int i){
    
    
        std::cout << "NoDefConstr::NoDefConstr(int) called\n";
    }
};
// variant类似于union,第一个参数必须拥有默认构造函数

std::variant<int, std::string> var{
    
    "hi"}; // initialized with string alternative
std::cout << var.index() << std::endl; // prints 1
var = 42; // now holds int alternative
std::cout << var.index() << std::endl; // prints 0
try {
    
    
  std::string s = std::get<std::string>(var); // access by type
  int i = std::get<0>(var); // access by index
}
catch (const std::bad_variant_access& e) {
    
     // in case a wrong type/index is used
  std::cout << "hello\n";
}

// std::variant<NoDefConstr_seven, int> v1; 第一个参数没有构造函数 编译失败
// std::monostate就是防止全部的参数都没有默认构造函数
std::variant<std::monostate, NoDefConstr_seven, int> v2;

输出:
1
0
hello

std :: opcional

Para obtener más información, consulte " C ++ Dry Goods Series-std :: opcional de C ++ 17 Nuevas funciones "

De hecho, es para evitar que devolvamos un número mágico que se establece aleatoriamente cuando no hay semántica en el código.

std :: cualquiera

Generalmente, como máximo las variantes son suficientes. ¿Por qué usar Any? Pero en casos extremos, usar any es mejor que usar void *. Se recomienda evitar la asignación dinámica de objetos pequeños.

std::any a = 1;
cout << a.type().name() << " " << std::any_cast<int>(a) << endl;
a = 2.2f;
cout << a.type().name() << " " << std::any_cast<float>(a) << endl;
if (a.has_value()) {
    
    
    cout << a.type().name() << std::endl;
}
a.reset();
// 可以这样判断类型
// assert(a1.type() == typeid(int));
if (a.has_value()) {
    
    
    cout << a.type().name() << std::endl;
}
a = std::string("a");
// 这个string的类型名是真的恶心
cout << a.type().name() << ": " << std::any_cast<std::string>(a) << endl;

输出:
i 1
f 2.2
f
NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE: a

Para obtener más información, consulte este artículo " C ++ 17 std :: any "

std :: aplicar

Creo que en realidad toma el valor del contenedor como entrada de la función.

int add_ten(int first, int second) {
    
     return first + second; }
auto add_ten_lambda = [](auto first, auto second) {
    
     return first + second; };

std::cout << std::apply(add_ten, std::pair(1, 2)) << '\n';
//std::cout << add(std::pair(1, 2)) << "\n"; // error
std::cout << std::apply(add_ten_lambda, std::tuple(2.2f, 3.0f)) << '\n';

输出:
3
5.2

Para obtener más información, consulte " Aplicar una función variadic con std :: apply "

std :: make_from_tuple

Use make_from_tuple para expandir tuple como un parámetro de constructor:

struct Foo {
    
    
    Foo(int first, float second, int third) {
    
    
        std::cout << first << ", " << second << ", " << third << "\n";
    }
};
int main() {
    
    
   auto tuple = std::make_tuple(42, 3.14f, 0);
   std::make_from_tuple<Foo>(std::move(tuple));
}

std :: string_view

Una característica que se puede utilizar a gran escala en el código habitual. De hecho, el debate sobre la cadena nunca se ha detenido. Mucha gente piensa que la cadena es una cadena de bytes en lugar de una cadena, porque la cadena se puede cambiar. Todos estos debates se pueden detener en C ++ 17. La complejidad del tiempo de construcción y substr de string_view es O (1), y no hay copia, porque substr es solo una operación de puntero.

Puede consultar los dos artículos siguientes:
" C ++ 17, use string_view para evitar la duplicación "
" Eficiencia y trampas de C ++ 17 string_view "

std :: file_system

Para obtener más información, consulte " sistema de archivos c ++ "

De hecho, el modelo es el file_system de boost, que apareció como muy pronto en 2003. Debido a que es multiplataforma, se puede decir que es muy cómodo.

Hay un dicho, no puedo ejecutar esto en mi máquina.

Código:

namespace fs = std::filesystem;
fs::path pathToShow("/home/lzl/Desktop/execise");
cout << "exists() = " << fs::exists(pathToShow) << "\n"
<< "root_name() = " << pathToShow.root_name() << "\n"
<< "root_path() = " << pathToShow.root_path() << "\n";

Después de ejecutar, muestra esto:

./a.out: /lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.26' not found (required by ./a.out)

Biblioteca de algoritmos paralelos

Se puede decir que esta es una de las características más importantes de C ++ 17. Esta característica agrega un parámetro de estrategia de ejecución a casi todas las funciones de biblioteca estándar, lo que permite a los usuarios elegir paralelo o serie, que incluye no solo siete nuevos algoritmos, y el tipo con el que estamos familiarizados.

Para obtener más información, consulte:
" [Traducción] C ++ 17, Algoritmos paralelos recientemente introducidos en la biblioteca estándar "
" Biblioteca de algoritmos paralelos STL "

constexpr si

Antes de C ++ 17, los determinantes lógicos como si no pueden aparecer en funciones genéricas

template <int N, int... Ns>
auto sum()
{
    
    
    if (sizeof...(Ns) == 0) // 若参数包为空, 直接返回 N
        return N;
    else                    // 否则进行递归调用
        return N + sum<Ns...>();
}

Esto fallará al compilar.

El enfoque anterior era utilizar la recursividad de la plantilla, así:

// 只有一个模板参数时调用此模板
template<int N>
int sum()
{
    
    
    return N;
}

// 模板参数 > 2 个时调用此模板
template <int N, int N2, int... Ns>
int sum()
{
    
    
    return N + sum<N2, Ns...>();
}

Ahora podemos hacer esto:

template <int N, int... Ns>
auto sum()
{
    
    
    if constexpr (0 == sizeof...(Ns))
        return N;
    else
        return N + sum<Ns...>();
}

Para obtener más información, consulte: " C ++ 17" constexpr if " "

referencia:

Supongo que te gusta

Origin blog.csdn.net/weixin_43705457/article/details/108903962
Recomendado
Clasificación