c++ - Black Magic 使用 Initializer_list 和包扩展

标签 c++ c++14 variadic-templates pack-expansion

扩展灵活的函数参数,有一种使用std::initializer_list的方法.然而我无法理解。任何人都可以以可以理解的方式解释这一点吗?

template<typename T, typename... Args>
auto print(T value, Args... args) {
    std::cout << value << std::endl;
    return std::initializer_list<T>{([&] {
        std::cout << args << std::endl;
    }(), value)...};
}

最佳答案

这是一种非常困惑的做事方式,但 C++14 要求我们做类似的事情。我将解释限制以及为什么以这种方式完成(尽管有更清晰的方法可以做到)。
此代码的目标是在单独的行上重复打印出它给出的每个参数。由于该函数是一个可变参数模板,它需要对表达式 std::cout << args << std::endl 使用包展开式.
您的第一个想法可能是 (std::cout << args << std::endl) ...; .但是,这实际上不是您在 C++14 中可以做的有效事情。实际上,您只能在 C++14 中以逗号分隔的值序列的上下文中执行包扩展,例如函数的参数列表或其他任何内容。你不能只是将一个包扩展为一个赤裸裸的声明。
嗯,你可以将包扩展到一个地方是一个支撑初始化列表(用于初始化对象的 {})。然而,{(std::cout << args << std::endl) ...};也不起作用。扩展没有任何问题;问题是支撑初始化列表本身。从语法上讲,braced-init-list 只能在初始化对象时出现。还有一个裸体{}因为语句不初始化任何东西。所以你不能在那里使用它。
所以你必须使用 {}初始化一些东西。典型的习惯用法是初始化一个空数组。例如:

int unused[] = {0, ((std::cout << args << std::endl), 0)...};
初始0,情况下需要 args是空的;你不能初始化一个没有元素的无大小数组。尾随 , 0在扩展表达式中是逗号表达式的一部分。
在 C++ 中,表达式 (1, 2)意思是“计算表达式 1,然后丢弃它的值,计算表达式 2,并将其用作总表达式的结果。”所以在包扩展中使用它意味着`输出一个参数,丢弃结果,并使用 0 作为表达式的结果。因此,就表达式的结果而言,每个包扩展只是表示“0”的一种非常奇特的方式。
最后,unused只存储一堆零。我们使用 unused 初始化的副作用作为强制 C++ 解压缩包扩展的一种方式。
在您显示的代码中,用户决定使用braced-init-list 直接初始化initializer_list<T> .这也是有效的,它有一个小好处,就是在一个空的 args 上工作。 .问题是用户然后返回这个对象。这很糟糕,因为实际上没有人可以使用该返回值。initializer_list s 不拥有他们引用的对象。在保存这些对象的支撑初始化列表的引用处创建一个临时数组; initializer_list只是指向该数组。临时数组将在 return 结束时销毁语句,所以调用者会得到一个 initializer_list指向一堆被破坏的对象。此外,它调用 T 的复制构造函数。很多次不必要的,因为没有人可以使用返回值。
所以这是一个常见习语的困惑和糟糕的例子。最好去掉return并将其设为 void功能。
C++17 只是让我们直接执行此操作而无需初始化数组:
((std::cout << args << std::endl), ...);
这是一个 fold expression在逗号运算符上。它将首先对 args 中的值调用每个子表达式。 .

关于c++ - Black Magic 使用 Initializer_list 和包扩展,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63885413/

相关文章:

c++ - 在什么情况下,c++的析构函数不会被调用?

C++ 转义短语子串

c++ - 如何实现自动插入隐含占位符的 easy_bind() ? *带有成员指针*

c++ - 如何测试 get_children() 返回的小部件的类型?

c++ - 绑定(bind)可变模板参数时拷贝过多

c++ - 线程可以被重用来运行可变参数函数吗?

c++ - 在 OpenGL 中重新组织图像/图片数组以适应 2 次方纹理大小

c++ - 关于 while() 中的 isalpha() 是微不足道的

c++ - 我应该移动 std::exchange ed 成员吗?

c++ - gcc 中变量模板的错误显式模板特化