Guía de desarrollo de Boost-4.3 opcional

opcional

En el proceso de desarrollo de software real, a menudo nos encontramos con "valores no válidos", por ejemplo, las funciones no siempre devuelven valores válidos, muchas veces la función se ejecuta correctamente, pero el resultado no es un valor razonable. Si se explica en lenguaje matemático, el valor de retorno está fuera del espacio de solución de la función.

Encontrar el recíproco de un número, tomar la raíz cuadrada de un dominio de números reales y encontrar una subcadena en una cadena pueden devolver "valores no válidos". En algunos casos de devoluciones no válidas, se puede notificar al usuario lanzando una excepción, pero en algunos casos esto es muy costoso o no se permiten excepciones, en cuyo caso se debe notificar al usuario de alguna manera razonable y eficiente.

La forma más común de representar "valores no válidos" es agregar una función "centinela", que se ubica fuera del espacio de la solución, como NULL, -1, EOF, string::npos, vector::end(), etc. Pero estos métodos no son lo suficientemente generales y, en muchos casos, no hay "centinelas" fuera del espacio de la solución. Otro método es utilizar pair<T, bool>un valor bool adicional para marcar si el valor es válido, al igual que la función de inserción del conjunto de contenedores estándar.

Opcional utiliza la semántica de "contenedor" para envolver objetos que "pueden producir valores no válidos" e implementa el concepto de "no inicializado", proporcionando una mejor solución a esta situación de "valores no válidos". Se ha propuesto su inclusión en el estándar C++17.

Opcional se encuentra en el espacio de nombres boost. Para utilizar opcional, debe incluir el archivo de encabezado <boost/optional.hpp>, es decir:

#include <boost/optional.hpp>
using namespace boost;

Resumen de clase

La biblioteca opcional primero define la constante boost::none, lo que aclara el significado de "valor no válido":

namespace detail {
    
     struct none_helper(); }
typedef int detail::none_helper::*none_t; //定义类型none_t
none_t const none = (static_cast<none_t>(0)); //定义常量none

boost::none es algo similar al puntero nulo nullptr en el estándar C++ 11, lo que significa que no está inicializado. Su tipo none_t es en realidad un puntero de variable miembro, que "apunta" al miembro int de detalle::none_helper eso no existe.

La clase principal de la biblioteca opcional es opcional, que se parece mucho a un contenedor que solo puede almacenar un elemento e implementa el concepto de "no inicializado": si el elemento no está inicializado, entonces el contenedor está vacío; de lo contrario, el contenedor es un valor válido y ya inicializado.

El resumen de la clase opcional es el siguiente:

template<class T>
class optional
{
    
    
public:
   optional(); //构造函数
   optional(none_t);
   optional(T const& v);
   optional(bool condition, T v);
   optional& operator= (T const& rhs); //赋值操作符
   template<class... Args>
   void emplace(Args...&& args); //就地创建
   T* operator->(); //重载操作符
   T& operator*();
   T& get(); //访问值
   T* get_ptr();
   T& value(); //访问值,可能抛出异常
   T const& value_or(T const& default) const ;
   template <typename F>
   T value_or_eval(F f) const;
   explicit operator bool() const; //显式bool转型
   bool operator!() const; // bool测试
};

La interfaz real de opcional es muy compleja porque debe poder envolver cualquier tipo, pero la interfaz real es relativamente simple y fácil de entender, y se explicará en detalle a continuación.

Función de operación

El parámetro de tipo de plantilla opcional r puede ser de cualquier tipo, al igual que los requisitos de un contenedor estándar para elementos. T no necesita tener un constructor predeterminado, pero debe ser copiable, porque opcional necesita copiar el valor internamente.

Los objetos opcionales se pueden crear de muchas maneras, por ejemplo:
1) opcional() sin parámetros u opcional(boost::none) construye un objeto opcional no inicializado;
2) opcional(v) construye un objeto opcional inicializado y copia v internamente el valor. Si el tipo de plantilla es T&, entonces opcional contiene internamente el paquete de la referencia;
3) opcional (condición, v) construye el objeto opcional de acuerdo con la condición. Si la condición es verdadera (verdadera), se inicializa en v, de lo contrario no está inicializado; 4
) Opcional admite operaciones de construcción y asignación de copias, y puede construirse a partir de otro objeto opcional;
5) emplace() es una función especial de "asignación" que puede crear objetos en el lugar usando parámetros, evitando el costo de copiar después construcción.
6) Si desea restaurar un objeto opcional a un estado no inicializado, puede asignar un valor de ninguno al objeto.

Opcional usa la semántica de puntero para acceder a elementos almacenados internamente, lo que hace que opcional se comporte como un puntero nulo cuando no está inicializado. Puede usar el operador explícito bool() y operador!() para detectar si opcional es válido.

Opcional también sobrecarga operador* y operador-> para lograr las mismas operaciones que los punteros. get() y get_ptr() pueden obtener referencias y punteros a elementos en forma de funciones. Nota: Solo usan BOOST_ASSERT internamente para brindar garantías de seguridad básicas. Si el opcional no se inicializa, el comportamiento de la función no está definido.

opcional también proporciona tres funciones miembro de la serie value(), que son más seguras que operator* y operator->:
1) value() también puede acceder a elementos, pero si opcional no se inicializa, se generará una excepción bad_optional_access;
2) value_or( default) Se garantiza que devolverá un valor válido. Si se ha inicializado opcional, entonces se devuelve el elemento interno, de lo contrario se devuelve default; 3
) value_or_eval(f) es similar a value_or(), pero su parámetro es una función invocable o objeto de función Si es opcional Si no se inicializa, se devuelve el resultado de la ejecución de f, que es f ().

Opcional también es totalmente compatible con operaciones de comparación. A diferencia de la "comparación superficial" de la comparación de punteros ordinaria (que solo compara valores de puntero), la comparación de opcional es una "comparación profunda" y también agrega juicio sobre condiciones no inicializadas.

uso

La interfaz de opcional es simple y clara, solo piense en él como un contenedor con un tamaño de 1 y se comporta como un puntero, o considérelo como un puntero inteligente como Scoped_ptr y Shared_ptr (pero tenga cuidado, opcional no es un puntero inteligente). , y el uso es similar pero el propósito es diferente).

El código que demuestra el uso básico de opcional es el siguiente:

#include <boost/optional.hpp>
using namespace boost;
int main()
{
    
    
	optional<int> op0; //一个未初始化的optional对象
	optional<int> op1(none); //同上,使用none赋予未初始化值

	assert(!op0); //bool测试
	assert(op0 == op1); //比较两个optional对象
	assert(op1.value_or(253) == 253); //获取缺省值

	cout << op1.value_or_eval( //使用函数对象
		[]() {
    
    return 874; }) << endl; //lambda表达式定义函数对象

	optional<string> ops("test"); //初始化为字符串test
	cout << *ops << endl; //用解引用操作符获取值

	ops.emplace("monado", 3); //就地创建一个字符串,没有拷贝代价
	assert(*ops == "mon"); //只使用了前三个字符

	vector<int> v(10);
	optional<vector<int>& > opv(v); //容纳一个容器的引用
	assert(opv); //bool转型

	opv->push_back(5); //使用箭头操作符操纵容器
	assert(opv->size() == 11);

	opv = none; //置为未初始化状态
	assert(!opv); //此时为无效值
}

Este código demuestra algunas operaciones básicas de opcional. A continuación, veamos un ejemplo un poco más complejo. El código utiliza opcional como valor de retorno de la función, lo que resuelve varios problemas planteados al principio de esta sección:

optional<double> calc(int x) //计算倒数
{
    
    
	return optional<double>(x != 0, 1.0 / x); //条件构造函数
}

optional<double> sqrt_op(double x) //计算实数的平方根
{
    
    
	return optional<double>(x > 0, sqrt(x)); //条件构造函数
}

int main()
{
    
    
	optional<double> d = calc(10);

	if (d) //bool语境测试optional的有效性
	{
    
    
		cout << *d << endl;
		cout << d.value() << endl;
	}

	d = sqrt_op(-10);
	if (!d) //使用重载的逻辑非操作符
	{
    
    
		cout << "no result" << endl;
	}
}

Función de fábrica

Opcional proporciona una función de fábrica make_optional() similar a make_pair() y make_shared(), que puede deducir automáticamente el tipo opcional según el tipo de parámetro para ayudar en la creación de objetos opcionales. Su declaración es:

optional<T> make_optional(T const& v);
optional<T> make_optional(bool condition, T const& v);

Sin embargo, make_optional() no puede derivar un objeto opcional de tipo de referencia T. Si necesita un optional<T&>objeto, no puede utilizar la función make_optional().

make_optional() tampoco admite el uso de emplace y puede haber un costo de copia del valor.

int main()
{
    
    
	auto x = make_optional(5); //使用auto关键字自动推导类型
	assert(*x == 5);

	auto y = make_optional<double>((*x > 10), 1.0); //模板参数明确类型
	assert(!y);
}

ejemplo de código

#include <cmath>
#include <type_traits>
#include <iostream>
using namespace std;

#define BOOST_DISABLE_ASSERTS
#include <boost/optional.hpp>
using namespace boost;

//
void case1()
{
    
    
	cout << typeid(none).name() << endl;
	cout << std::is_member_object_pointer<none_t>::value << endl;
}

//
void case2()
{
    
    
	optional<int> op0;
	optional<int> op1(none);

	assert(!op0);
	assert(op0 == op1);
	assert(op1.value_or(253) == 253);

	cout << op1.value_or_eval(
		[]() {
    
    return 874; }) << endl;

	optional<string> ops("test");
	cout << *ops << endl;

	ops.emplace("monado", 3);
	assert(*ops == "mon");

	vector<int> v(10);
	optional<vector<int>& > opv(v);
	assert(opv);

	opv->push_back(5);
	assert(opv->size() == 11);

	opv = none;
	assert(!opv);
}

//
optional<double> calc(int x)
{
    
    
	return optional<double>(x != 0, 1.0 / x);
}

optional<double> sqrt_op(double x)
{
    
    
	return optional<double>(x > 0, sqrt(x));
}

void case3()
{
    
    
	optional<double> d = calc(10);

	if (d)
	{
    
    
		cout << *d << endl;
		cout << d.value() << endl;
	}

	d = sqrt_op(-10);
	if (!d)
	{
    
    
		cout << "no result" << endl;
	}
}

//
void case4()
{
    
    
	auto x = make_optional(5);
	assert(*x == 5);

	auto y = make_optional<double>((*x > 10), 1.0);
	assert(!y);
}


//
int main()
{
    
    
	case1();
	case2();
	case3();
	case4();
}

Insertar descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/qq_36314864/article/details/132215759
Recomendado
Clasificación