【优秀三方库研读】【C++基础知识】odygrd/quill -- 折叠表达式
compute_encoded_size_and_cache_string_lengths 方法中这段代码是一个C++的折叠表达式(fold expression)的应用,用于计算多个参数编码后的总大小。下面我将详细解释这段代码的每个部分,并说明为什么这样写。
代码如下:
size_t total_sum{0};// Avoid using a fold expression with '+ ...' because we require a guaranteed evaluation// order to ensure that each argument is processed in sequence. This is essential for// correctly populating the conditional_arg_size_cache((total_sum += Codec<remove_cvref_t<Args>>::compute_encoded_size(conditional_arg_size_cache, args)), ...);return total_sum;
代码分解
-
变量定义:
size_t total_sum{0};
- 定义一个
size_t
类型的变量total_sum
,初始化为0。这个变量用于累加所有参数的编码后大小。
- 定义一个
-
折叠表达式:
((total_sum += Codec<remove_cvref_t<Args>>::compute_encoded_size(conditional_arg_size_cache, args)), ...);
- 这是一个逗号操作符的折叠表达式(comma-fold expression),展开形式为:
(total_sum += Codec<remove_cvref_t<Args1>>::compute_encoded_size(conditional_arg_size_cache, args1)), (total_sum += Codec<remove_cvref_t<Args2>>::compute_encoded_size(conditional_arg_size_cache, args2)), ..., (total_sum += Codec<remove_cvref_t<ArgsN>>::compute_encoded_size(conditional_arg_size_cache, argsN));
- 对参数包
Args
中的每一个类型Args_i
,调用Codec<remove_cvref_t<Args_i>>::compute_encoded_size
计算其编码大小,并将结果累加到total_sum
中。 remove_cvref_t<Args_i>
用于移除类型的const
、volatile
和引用修饰符,确保Codec
模板实例化时使用的是原始类型。
- 这是一个逗号操作符的折叠表达式(comma-fold expression),展开形式为:
-
conditional_arg_size_cache
:- 这是一个缓存结构(可能是
std::vector<size_t>
或类似容器),用于存储某些条件参数的编码大小。compute_encoded_size
函数可能会根据参数类型和值更新这个缓存。
- 这是一个缓存结构(可能是
-
返回结果:
return total_sum;
- 返回累加后的总大小。
为什么这样写?
-
避免使用
+ ...
折叠表达式:- 注释中明确提到不使用
+ ...
折叠表达式(即(Codec<Args>::compute_encoded_size(args) + ...)
),因为需要保证求值顺序。 - C++中,
+
操作符的求值顺序是未指定的(unspecified),编译器可以自由重排操作数的求值顺序。如果compute_encoded_size
有副作用(比如更新conditional_arg_size_cache
),求值顺序的乱序会导致缓存内容错误。 - 而逗号操作符(
,
)的求值顺序是严格从左到右的(C++17起),因此((expr), ...)
能保证每个expr
按参数包的顺序依次求值。
- 注释中明确提到不使用
-
依赖
conditional_arg_size_cache
的副作用:- 如果
compute_encoded_size
会根据当前参数的值更新缓存(例如,某些参数的编码大小依赖于之前参数的值),则必须保证参数的处理顺序与传入顺序一致。 - 使用逗号折叠表达式是实现这一目标的简洁方式。
- 如果
-
简洁性与性能:
- 折叠表达式是C++17的特性,能以最简洁的方式展开参数包。
- 直接累加到
total_sum
避免了临时变量的分配,性能更高。
等价实现(非折叠表达式)
如果不使用折叠表达式,代码可能需要写成:
size_t total_sum{0};
auto update_sum = [&](const auto& arg) {total_sum += Codec<remove_cvref_t<decltype(arg)>>::compute_encoded_size(conditional_arg_size_cache, arg);
};
(update_sum(args), ...); // 仍然用逗号操作符保证顺序
return total_sum;
或更冗长的递归模板展开。折叠表达式是更优的选择。
总结
这段代码的核心目的是:
- 按顺序计算每个参数的编码大小。
- 确保
conditional_arg_size_cache
的更新顺序与参数顺序一致。 - 以最高效的方式累加结果。
通过逗号折叠表达式,既保证了求值顺序,又实现了简洁高效的代码。