核心概念#

本集主要讨论 C++17 引入的 std::optional。它提供了一种优雅的方式来处理==“值可能存在,也可能不存在”==的情况,避免了使用魔术数字(如 -1)、空字符串或布尔标志位等不直观的方法。

1. 传统处理方式的弊端#

在没有 std::optional 之前,如果一个函数可能无法返回有效数据,程序员通常会:

  • 返回特殊值:例如找不到文件时返回空字符串 ""。但问题是,有时空字符串本身可能也是合法的返回结果,导致二义性。
  • 使用布尔标记:通过引用的方式传出数据,函数返回 bool 表示是否成功。这种方式虽然可行,但代码写起来比较琐碎。
    • 函数返回一个布尔值(true 或 false)来表示操作是否成功,而实际的数据通过“输出参数”(通常是引用或指针)来传递。

2. std::optional 的基本用法#

std::optional 像是一个容器,它要么包含一个指定类型的值,要么为空(std::nullopt)。

  • 包含头文件#include <optional>
  • 读取文件示例
    std::optional<std::string> ReadFileAsString(const std::string& filepath) {
        std::ifstream stream(filepath);
        if (stream) {
            std::string result;
            // 读取文件内容...
            return result;
        }
        //return {};// ai提示return std::nullopt; // 显式表示没有值 [00:05:00]
    }

完整代码#

//同文件夹下:data.txt
Data!Hello!
#ifdef LY_EP76

#include <iostream>   
#include <fstream>
//C++17引入了std::optional类型,可以用来表示一个值可能存在也可能不存在的情况。它提供了一种更安全和更清晰的方式来处理函数返回值,避免了使用引用参数来传递成功与否的信息。
#include <optional>
#include <sstream> //读取文件内容需要使用std::stringstream


//方法1:通过引用参数返回成功与否
//std::string ReadFileAsString(const std::string& filePath,
//bool& outSuccess)
std::optional<std::string> ReadFileAsString(const std::string& filePath)
{
	std::ifstream stream(filePath);
	if (stream)
	{
		std::stringstream result;
		result << stream.rdbuf();
		//read file
		stream.close();
		return result.str();
	} 
	return {};
}

int main()
{

	//在内存中,std::optional<T> 通常包含两个主要部分:
	//1. 一块足够大的内存空间:用来存放类型为 T 的值(在你的例子中是 std::string)。
	//2. 一个布尔标记(bool flag):记录这个盒子现在是“满的”还是“空的”
	std::optional<std::string> data = ReadFileAsString("data.txt");
	std::optional<std::string> data1 = ReadFileAsString("data1.txt");

	//如果是读取参数,那就是指定默认值
	//std::string value = data.value_or("default value");

	//或者if(data)
	if(data.has_value())
	{
		//数据读取
		std::string& string = *data;
		std::cout << "File content: " << data.value() << std::endl;
		std::cout << "File content: " << string << std::endl;
	}
	else
	{
		std::cout << "Failed to read file." << std::endl;
	}


	if (data1.has_value())
	{
		//数据读取
		std::string& string = *data1;
		std::cout << "File content: " << data1.value() << std::endl;
		std::cout << "File content: " << string << std::endl;
	}
	else
	{
		std::cout << "Failed to read file(data1.txt)." << std::endl;
	}
	std::cin.get();

} 
/*
File content: Data!Hello!
File content: Data!Hello!
Failed to read file(data1.txt).
*/
#endif

3. 如何检查与获取值#

调用返回 std::optional 的函数后,有几种处理方式:

  • 布尔转换:可以直接用 if (data) 检查是否有值。
  • .value() 方法:获取包含的值。如果值不存在,会抛出异常。
  • * 运算符:像指针一样解引用获取值,但不安全(不检查是否存在)。
  • .value_or() 方法(重点推荐) 如果有值则返回该值,否则返回你提供的默认值。这非常适合设置备选项。

4. 优势总结#

  • 代码意图清晰:返回值类型直接告诉调用者“这个值可能为空”。
  • 安全性:强制调用者考虑“无值”的情况,减少潜在的崩溃风险。
  • 语义化:相比于返回 -1 或指针,optional 的表达力更强,且不需要处理堆分配。

本集要点: 当你需要表示“无结果”这一状态,且不想引入复杂逻辑或歧义时,请首选 std::optional