没有 typedef 的可变参数扩展器
Variadic expander without a typedef
常用于执行可变参数扩展的一个技巧是将未调整大小的数组 typedef 与逗号运算符结合使用,如下所示:
#include <iostream>
template<typename... Ts>
void expander(Ts&&... ts)
{
using expand = int[];
(void)expand{0, (std::forward<Ts>(ts)(), 0)...};
}
void f()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main()
{
expander(f, f);
}
我们可以不引入 typedef 来做到这一点吗? 如果我直接尝试
(void)int[]{0, (std::forward<Ts>(ts)(), 0)...};
gcc/clang吐出来
error: expected primary-expression before 'int'
如果我尝试加上括号,代码会编译,但我认为它不符合标准:
warning: ISO C++ forbids compound-literals [-Wpedantic]
Can we do this without introducing a typedef?
没有 typedef,但有变量
template<typename... Ts>
void expander(Ts&&... ts)
{
int dummy[] = {0, (std::forward<Ts>(ts)(), void(), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable
}
在 C++17 中,您可以使用折叠来实现:
template<typename... Ts>
void expander(Ts&&... ts) {
(..., std::forward<Ts>(ts)());
}
在这个表达式中
(void)int[]{0, (std::forward<Ts>(ts)(), 0)...};
您正在尝试使用函数式符号转换来创建临时数组。这行不通,因为该语言只允许函数符号与 简单类型说明符 或 类型名称说明符 一起使用。
Similarly, a simple-type-specifier or typename-specifier followed by a braced-init-list creates a temporary object of the specified type direct-list-initialized (8.5.4) with the specified braced-init-list, and its value is that temporary object as a prvalue.
如果您在 [dcl.type.simple] 下查找 simple-type-specifier 的定义,它不包含任何带有数组括号的内容。事实上,它甚至不包括超过一个词的任何内容。这就是为什么写 int(1)
是有效的,但 signed int(1)
不是。
因此,对于 C++11/14,您需要一个 typedef
或声明一个数组变量。但是,使用 C++1z 编译器,您可以使用折叠表达式并避免两者
template<typename... Ts>
void expander(Ts&&... ts)
{
(void(std::forward<Ts>(ts)()), ...);
}
您可以按照以下方式进行:
#include <iostream>
template<typename... Ts>
void expander(Ts&&... ts)
{
int dummy[sizeof...(Ts)] = {(std::forward<Ts>(ts)(), 0)...};
}
void f()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main()
{
expander(f, f, f);
expander();
}
常用于执行可变参数扩展的一个技巧是将未调整大小的数组 typedef 与逗号运算符结合使用,如下所示:
#include <iostream>
template<typename... Ts>
void expander(Ts&&... ts)
{
using expand = int[];
(void)expand{0, (std::forward<Ts>(ts)(), 0)...};
}
void f()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main()
{
expander(f, f);
}
我们可以不引入 typedef 来做到这一点吗? 如果我直接尝试
(void)int[]{0, (std::forward<Ts>(ts)(), 0)...};
gcc/clang吐出来
error: expected primary-expression before 'int'
如果我尝试加上括号,代码会编译,但我认为它不符合标准:
warning: ISO C++ forbids compound-literals [-Wpedantic]
Can we do this without introducing a typedef?
没有 typedef,但有变量
template<typename... Ts>
void expander(Ts&&... ts)
{
int dummy[] = {0, (std::forward<Ts>(ts)(), void(), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable
}
在 C++17 中,您可以使用折叠来实现:
template<typename... Ts>
void expander(Ts&&... ts) {
(..., std::forward<Ts>(ts)());
}
在这个表达式中
(void)int[]{0, (std::forward<Ts>(ts)(), 0)...};
您正在尝试使用函数式符号转换来创建临时数组。这行不通,因为该语言只允许函数符号与 简单类型说明符 或 类型名称说明符 一起使用。
Similarly, a simple-type-specifier or typename-specifier followed by a braced-init-list creates a temporary object of the specified type direct-list-initialized (8.5.4) with the specified braced-init-list, and its value is that temporary object as a prvalue.
如果您在 [dcl.type.simple] 下查找 simple-type-specifier 的定义,它不包含任何带有数组括号的内容。事实上,它甚至不包括超过一个词的任何内容。这就是为什么写 int(1)
是有效的,但 signed int(1)
不是。
因此,对于 C++11/14,您需要一个 typedef
或声明一个数组变量。但是,使用 C++1z 编译器,您可以使用折叠表达式并避免两者
template<typename... Ts>
void expander(Ts&&... ts)
{
(void(std::forward<Ts>(ts)()), ...);
}
您可以按照以下方式进行:
#include <iostream>
template<typename... Ts>
void expander(Ts&&... ts)
{
int dummy[sizeof...(Ts)] = {(std::forward<Ts>(ts)(), 0)...};
}
void f()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main()
{
expander(f, f, f);
expander();
}