C++17: deducción de tipo decltype

Cebador

En el proceso de programación, a veces necesitamos declarar variables de acuerdo con el tipo de expresión, especialmente cuando se trata de programación de plantillas y programación genérica, a menudo encontramos problemas de este tipo: (1), algunos tipos genéricos están determinados por parámetros de plantilla, pero es difícil o imposible de expresar; (2), el tipo de variable debe determinarse en tiempo de compilación.

Además, sabemos que auto ignora los modificadores de tipo durante la deducción automática de tipo. Esto dará lugar a incoherencias entre el tipo de autoderivación y el tipo de expresión original.

Para resolver mejor estos problemas, a partir del estándar C++ 11, C++ introdujo la palabra clave decltype, que se usa para permitir que el compilador identifique el tipo de expresión en el momento de la compilación, facilitar la derivación de tipos y también resolver problemas genéricos. con representación de tipo variable en programación y programación de plantillas.

evolución estándar

decltypeEs la abreviatura de tipo de declaración. El estándar C++11 presenta las funciones principales y las reglas de derivación de decltype, y cada estándar posterior a C++11 amplía y mejora las reglas autodefinidas de C++11. El proceso de evolución específico es el siguiente:

  • C++11: Introducir palabras clave e introducir decltypefunciones básicas para deducir el tipo de variables basadas en expresiones;
  • C++14: introduce dos mejoras importantes
    • Introducir decltype(auto)la sintaxis que se puede utilizar para la deducción del tipo de retorno de función. Según decltype(auto)la sintaxis, el tipo de valor devuelto de una función se puede deducir de la expresión de valor devuelto del cuerpo de la función, lo que simplifica la declaración del tipo de valor devuelto de la función.
    • Restricciones relajadas sobre tipos incompletos: en C++ 11, si decltypela expresión deducida da como resultado un tipo incompleto, provocará un error de compilación. En C++14, el manejo de tipos incompletos es más relajado, permitiendo el uso de decltypevariables deducidas de tipos incompletos.
  • C++17: decltype(atuo) admite marcadores de posición de parámetros de plantilla que no son de tipo.

C++11

Introduzca palabras clave y decltypefunciones básicas para deducir el tipo de una variable en función de una expresión; cuando se utiliza para deducir decltype(e)el tipo de una expresión e (tipo T), las decltypereglas de derivación definidas por el estándar C++11 son las siguientes:

  1. Si es una expresión de identificador sin paréntesis o un acceso de miembro de clase, entonces decltype(e) deduce que e es de tipo T; si no existe tal entidad o si e es un conjunto de funciones sobrecargadas, entonces decltype(e) no puede Derivación. Y los calificadores const/volatile del proceso de derivación serán ignorados;
  2. Si e es un objeto invocable, entonces decltype(e) deduce el tipo del valor de retorno del objeto invocable;
  3. Si e es un valor l, decltype(e) deduce T&. los calificadores const/volatile no se pueden ignorar;
  4. Si e es un valor x, decltype(e) se deduce como T&&, los calificadores const/volatile no se pueden ignorar;
  5. Si decltype(e) no puede coincidir con los 4 casos anteriores, decltype(e) se deducirá como el tipo T de e;

Para que todos entiendan estas cinco reglas más vívidamente, ilustraremos estas cinco reglas de derivación a través de algunos ejemplos a continuación.

Ejemplo 1: expresión de identificador sin corchetes

int x = 42;
decltype(x) y; // 推导结果是 int,满足第1条规则

Ejemplo 2: Expresiones de identificador entre paréntesis

int x = 42;
decltype((x)) y = x; // 推导结果是 int&,满足第三条规则

Ejemplo 3: acceso de miembro de clase sin paréntesis

struct MyClass {
    
    
    int member;
};

const MyClass obj;
decltype(obj.member) result = obj.member; // 推导结果是 int, 忽略const/volatile 限定符,满足第1条规则

Ejemplo 4: acceso de miembro de clase entre paréntesis

struct MyClass {
    
    
    int member;
};

const MyClass obj;
decltype((bj.member)) result = obj.member; // 推导结果是 const int&, const/volatile 限定符不能忽略,满足第3条规则

Ejemplo 5: Expresiones de objetos invocables

int add(int a, int b)
{
    
    
    return a + b;
}

decltype(add(1, 2)) result; // 推导结果是 int,满足第2条规则

Ejemplo 6: Cambiando el valor de x

int x = 42;
decltype(std::move(x)) result = std::move(x); // 推导结果为int&&,std::move(x) 为将亡值

Ejemplo 7: expresiones rvalue

int x = 42;
decltype(x + 1) result; // 推导结果是 int(右值表达式 x + 1 的类型是 int)

Ejemplo 8: variable de referencia rvalue

int&& i = 500;
decltype(i) x2;           // x2的类型是int&&,满足第5条

C++14

C++14 introduce principalmente dos mejoras importantes, que son: relajación de las restricciones sobre tipos incompletos; introducción de decltype(auto)sintaxis.

Relajar las restricciones sobre los tipos incompletos

El estándar C++11 requiere que, decltypecuando se use, la expresión deducida sea un tipo completo. Si decltypela expresión deducida es un tipo incompleto, como una declaración de una clase que aún no se ha definido, se producirá un error de compilación. C++14 relaja esta restricción, permitiendo decltypededucir variables con tipos incompletos. Esto hace que sea más conveniente escribir algún código de plantilla específico, porque en algunos casos, es posible que sea necesario deducir tipos incompletos.

Sin embargo, aunque C ++ 14 ha relajado las restricciones sobre los tipos incompletos, aún requiere que la expresión deducida sea visible cuando se usa, es decir, al menos el tipo debe declararse hacia adelante cuando se deduce. De lo contrario, se producirán errores de compilación.

Aquí hay un ejemplo que muestra cómo usar decltype para deducir un tipo incompleto en programación genérica:

template <typename T>
struct Container
{
    
    
    using ValueType = decltype(*std::declval<T>()); // 使用 decltype 推导不完整类型
    // 其他成员和函数...
};

int main()
{
    
    
    Container<std::vector<int>> container;
    using ValueType = typename decltype(container)::ValueType; // 推导结果为 int&
    return 0;
}

tipo de declaración (automático)

Además de relajar las restricciones sobre los tipos incompletos, C++14 tiene otra característica decltype(auto). decltype(auto)La función es decirle al compilador que las reglas de derivación de auto siguen a decltype en lugar de auto. Sin embargo, una cosa a tener en cuenta es que decltype(auto) debe declararse por separado y no puede combinarse con otros. Así que las siguientes declaraciones son ilegales: decltype(auto)*, const decltype(auto), volatile decltype(auto).

decltype(auto)Las reglas de derivación son las siguientes:

  1. si la expresión-inicialización es una expresión de identificador, entonces decltype(auto)se deduce el tipo de la expresión (no se pueden ignorar los calificadores constantes/volátiles y los modificadores de referencia);
  2. si la expresión-inicialización es una expresión-llamada-función, entonces decltype(auto)se deduce el tipo de retorno de la expresión-llamada-función;
  3. Si la expresión de inicialización es una expresión lvalue (como nombre de variable, nombre de matriz, acceso de miembros, etc.), se decltype(auto)deduce como el tipo de referencia del tipo lvalue correspondiente (los calificadores const/volátiles y los modificadores de referencia no se pueden ignorar).
  4. Si la expresión de inicialización es una expresión de valor r (como un valor literal, un objeto temporal, el resultado de una expresión, etc.), entonces se decltype(auto)deduce como el tipo correspondiente al valor r (los calificadores constantes/volátiles y los modificadores de referencia no pueden ser ignorado).
  5. Si la expresión de inicialización es un valor x (como una asignación de movimiento), entonces decltype(auto)se deduce como una referencia de valor r del tipo correspondiente

Ejemplo 1: expresión de identificador

int x = 42;
decltype(auto) y = x; // 推导结果是 int(x 的类型)

Ejemplo 2: expresión de llamada de función

int add(int a, int b)
{
    
    
    return a + b;
}

decltype(auto) result = add(1, 2); // 推导结果是 int(add 函数返回类型)

Ejemplo 3: expresiones lvalue

const int x = 42;
decltype(auto) ref = (x); // 推导结果是 const int&(x 的引用类型)

Ejemplo 4: Expresiones de Rvalue

decltype(auto) x2 = 50; // 推导结果是 int

Ejemplo 4: Cambiando el valor de x

int x2 = 50;
decltype(auto) x3 = std::move(x2); // 推导结果为int&&

Además de la deducción de tipo variable, decltype(auto) se introdujo en C++14 como una sintaxis de tipo de devolución. Se utiliza en la declaración de la función para especificar el tipo de devolución que se deducirá de la expresión en el cuerpo de la función.

Para comprender mejor la sintaxis de decltype(auto) como tipo de devolución, nos referimos a los siguientes tres métodos de derivación automática y definición de tipos de devolución de función.

El primero: C ++ 14 deduce automáticamente el tipo de valor devuelto en función de la nueva función automática

template<typename Container, typename Index>
auto accessOrUpdate(Container& c, Index i) {
    
     
  return c[i];  // 返回类推导为c[i]的类型,而且会异常引用限制      
}

std::vector<int> v{
    
    1,2,3,4,5};
accessOrUpdate(v,2) = 10;      // 编译错误,不允许赋值

El segundo tipo: C ++ 14 implementa la deducción del tipo de valor de retorno basada en auto y decltype

template <typename Container, typename Index>
auto accessOrUpdate(Container &c, Index i) -> decltype(c[i]) {
    
    
  return c[i];
}

std::vector<int> v{
    
    1,2,3,4,5};
accessOrUpdate(v,2) = 10;

El tercer tipo: C++14 decltype (auto) implementa la deducción del tipo de valor de retorno

template <typename Container, typename Index>
decltype(auto) accessOrUpdate(Container &c, Index i) {
    
    
  return c[i];
}

std::vector<int> v{
    
    1,2,3,4,5};
accessOrUpdate(v,2) = 10;

En comparación con la deducción del tipo de valor de retorno de las tres funciones anteriores, decltype(auto) permite que el compilador deduzca automáticamente el tipo de retorno de la función según el tipo de expresión sin especificar explícitamente el tipo de retorno. Este enfoque simplifica el código y hace que la derivación sea más flexible.

C++17

Para complementar auto, C++ 17 comenzó a admitir plantillas sin tipo decltype (auto). Sin embargo, debe tenerse en cuenta que en el estándar C++17, los tipos de parámetros de plantilla que no son de tipo deben ser tipos de colación (int, short, long, etc.), tipos de enumeración, tipos de puntero, tipos de referencia lvalue y std::nullptr_t , mientras que los tipos personalizados, los flotantes y las cadenas no están permitidos como parámetros de plantilla que no son de tipo.

template<decltype(auto) n>  // C++17 decltype(auto)形参声明
auto f() -> std::pair<decltype(n), decltype(n)> // auto 不能从花括号初始化器列表推导
{
    
    
    return {
    
    n, n};
}

f<5>();      // n为int
f<(5)>();    // n为int&
f<'a'>();    // n为char
f<('a')>();  // n为char&
f<1.0>();    // 编译失败double不能作为模板参数,double不允许做非类型模板参数。

C++20 permite tipos de clases literales como parámetros de plantilla que no son de tipo. Por ejemplo, antes de C++20, el siguiente código no se puede compilar, pero se puede compilar en C++20.

class A {
    
    };

template <A a>
class B {
    
    };

A a;
B<a> b;  // C++20 前编译失败,C++20 可以编译成功。

Resumir

Este artículo comienza con dos problemas comunes que se encuentran a menudo en la programación genérica y analiza paso a paso la palabra clave decltype introducida desde C++ 11. Espero que este artículo pueda ser útil para todos.

Guess you like

Origin blog.csdn.net/liuguang841118/article/details/130782888