为什么std :: min(std :: initializer_list <T>)按值获取参数?

gTc*_*TcV 8 c++ std min c++-standard-library c++11

阅读这个问题的答案,我惊讶地发现std::min(std::initializer_list<T>)它的价值取决于它.

如果你std::initializer_list以其名称隐含的方式使用,即作为某个对象的初始化者,我理解我们不关心复制它的元素,因为它们将被复制以初始化对象.但是,在这种情况下,我们很可能不需要任何副本,因此将参数看作std::initializer_list<const T&>是可能的更合理.

这种情况的最佳做法是什么?如果你不叫initializer_list的版本,std::min如果你关心不进行不必要的副本,或者是有一些其他的办法来避免复制?

M.M*_*M.M 6

没有这样的事情std::initializer_list<const T&>.初始化程序列表只能按值保存对象.

见[dcl.init.list]/5:

类型的对象std::initializer_list<E>是从初始化列表构造的,就好像实现分配了N个元素类型的临时数组const E,其中N是初始化列表中的元素数.

没有引用数组,因此也不能有引用数组initializer_list.

在这种情况下,我的建议是编写一个替换,std::min其中包含转发引用的可变参数模板.这有效(虽然我确信它可以改进):

template<typename T, typename... Args>
T vmin( T arg1, Args&&... args )
{
    T *p[] = { &arg1, &args... };

    return **std::min_element( begin(p), end(p), 
            [](T *a, T *b) { return *a < *b; } );
}
Run Code Online (Sandbox Code Playgroud)

看它工作 - 使用minmmin,制作两个额外的副本(for ab).


mfu*_*chs 4

总结一下评论:

\n\n

Anstd::initializer_list应该是一个轻量级代理,如 cppreference.com上所述。\n因此,初始化程序列表的副本应该非常快,因为不复制底层元素:

\n\n

C++11\xc2\xa718.9/2

\n\n
\n

类型的对象提供对\ninitializer_list<E>类型的对象数组的访问[注意: \n 一对指针或一个指针加一个长度将是 的明显表示。\n 用于实现 8.5.4 中指定的初始值设定项列表。复制初始值设定项列表不会复制基础元素。\xe2\x80\x94尾注]const E.initializer_listinitializer_list

\n
\n\n

不过,使用引用可以归结为使用指针,因此需要额外的间接寻址。

\n\n

因此,实际的问题std::min不是它采用initializer_list按值,而是如果参数initializer_list是在运行时计算的,则必须复制参数。[1]\n不幸的是,后来发现 [1] 的基准被破坏了。

\n\n
auto min_var = std::min({1, 2, 3, 4, 5}); // fast\nauto vec = std::vector<int>{1, 2, 3, 4, 5};\nmin_var = std::min({vec[0], vec[1], vec[2], vec[3], vec[4]}); // slow\n
Run Code Online (Sandbox Code Playgroud)\n\n

[1]:N2722页。2

\n