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

时间:2014-09-24 15:46:34

标签: c++ c++11 std min c++-standard-library

阅读this question的答案,我惊讶地发现std::min(std::initializer_list<T>)按价值得出了它的论点。

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

这种情况的最佳做法是什么?如果您不关心不制作不必要的副本,或者是否有其他一些技巧可以避免复制,您是否应该调用initializer_list std::min版本的版本?

2 个答案:

答案 0 :(得分:6)

没有std::initializer_list<const T&>之类的东西。初始化列表只能按值保存对象。

见[dcl.init.list] / 5:

  

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

没有引用数组,因此也不能有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; } );
}

See it working - 使用minmmin,制作了两份额外的副本(ab)。

答案 1 :(得分:3)

总结评论:

cppreference.com所述,std::initializer_list应该是轻量级代理。 因此,初始化程序列表的副本应该非常快,因为不会复制基础元素:

C ++11§18.9/ 2

  

initializer_list<E>类型的对象提供对const E.类型对象数组的访问   [注意:   一对指针或指针加上长度将是initializer_list的明显表示。   initializer_list用于实现8.5.4中指定的初始化列表。复制初始化列表不会复制基础元素。 - 结束记录]

使用引用虽然可以归结为使用指针,因此也是一个额外的间接。

因此,std::min的实际问​​题不在于它需要initializer_list的值,而是在运行时计算initializer_list的参数必须被复制。 [1] [1]中的基准[1]被打破了later是不幸的。

auto min_var = std::min({1, 2, 3, 4, 5}); // fast
auto vec = std::vector<int>{1, 2, 3, 4, 5};
min_var = std::min({vec[0], vec[1], vec[2], vec[3], vec[4]}); // slow

[1]:N2722 p。 2