深入探讨C++折叠表达式在极端情况下的使用

在之前的元编程系列文章中,讨论了折叠表达式。今天,将探索折叠表达式使用的极端情况。在开始之前,需要声明:本文中的代码示例展示了变参模板的使用,参数是通过值传递的,而不是转发。这样做是为了简化示例,专注于背后的思想。

空变参参数

在一元折叠(无初始化的折叠表达式)的情况下,对于三种类型的运算符是合法的:逻辑与(&&)、逻辑或(||)和逗号(,)。以下是每种运算符的示例。

在C++中,可以定义一个模板函数,它接受任意数量的参数,并使用逻辑与运算符进行折叠。如果调用时没有参数,函数将返回true。这背后的逻辑是,逻辑与运算符要求没有部分求值为false。在这种情况下,根本没有部分,因此没有部分求值为false,因此结果应该是true。

template<typename... Args> auto and_cond(Args... args) { return (args && ...); }

对于逻辑或运算符,如果调用时没有参数,函数将返回false。这背后的逻辑是,逻辑或运算符要求至少有一个部分求值为true。因为没有部分,所以没有部分求值为true,因此结果是false。

template<typename... Args> auto or_cond(Args... args) { return (args || ...); }

对于逗号运算符,如果没有传递参数,结果将是void()。这是因为逗号运算符用于分隔表达式,如果没有参数,就没有表达式可以分隔。

template<typename... Args> auto comma_op(Args... args) { return (args , ...); }

其他运算符

对于其他运算符,如果没有参数的调用将不会编译。

变参包中的单个参数

这里的情况有点出乎意料。当只向变参包传递一个参数时,结果将忽略运算符,并返回发送到函数的相同类型和参数(在gcc 13.1.0和clang 16.0.0上测试过)。例如:

template<typename... Args> auto func(Args... args) { return (args || ...); }

在main函数中调用:

int main() { using namespace std::string_literals; std::cout << func("I am a string"s); // 将打印 "I am a string" return EXIT_SUCCESS; }

根据C++ Insights生成的代码是:

template<> std::basic_string<char> func<std::basic_string<char>> ( std::basic_string<char> __args0 ) { return std::basic_string<char>( static_cast<std::basic_string<char>&&>(__args0)); }

对于任何其他运算符,包括+=、*和->,也是如此。

无参数的二元折叠表达式

对于二元折叠表达式,如果没有参数,将应用与单个参数的一元折叠表达式相同的规则:

template<typename... Args> auto func(Args... args) { return (args ->* ... ->* "I am a string"s); }

在main函数中调用:

int main() { std::cout << func(); // 将打印 "I am a string" return EXIT_SUCCESS; }

根据C++ Insights生成的代码是:

template<> std::basic_string<char> func<> () { return std::operator "" s( "I am a string", 13UL); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485