C++11的initializer_list

0. 序

今天写一道OJ的时候,发现死活超时,后来对比答案发现是因为我用了初始化列表导致超时了。因此写一篇博客记录一下。

1. 花括号统一初始化

一个容易混淆的概念,花括号初始化与initializer_list类。在effective modern C++中item 7详细阐述了这一点。之前一个郁闷的报错是

Widget w();  
Widget w{};

希望初始化一个没有参数的构造函数时,第一种初始化方法会报错,因为gcc会认为这是在声明一个函数。
通常在花括号初始化对象时,并不代表一定会产生相应的initializer_list实例。仅仅是调用相应的构造函数。但如果有相应的以initializer_list为参数的构造函数时,编译器会偏向于选择该构造函数。

class Widget{
public:
Widget(int i, double b);
Widget(std::initializer_list<double> a);
};

Widget w{10, 3.5}; // 调用第二个构造函数
Widget w(10, 3.5)  // 调用第一个构造函数

2. initializer_list的开销

首先要回答的一个问题是,initializer_list到底是一个什么样的类。可以从windows下的实现看到

template <class _Elem>
class initializer_list {
public:
    using value_type      = _Elem;
    using reference       = const _Elem&;
    using const_reference = const _Elem&;
    using size_type       = size_t;

    using iterator       = const _Elem*;
    using const_iterator = const _Elem*;

    constexpr initializer_list() noexcept : _First(nullptr), _Last(nullptr) {}

    constexpr initializer_list(const _Elem* _First_arg, const _Elem* _Last_arg) noexcept
        : _First(_First_arg), _Last(_Last_arg) {}

    _NODISCARD constexpr const _Elem* begin() const noexcept {
        return _First;
    }

    _NODISCARD constexpr const _Elem* end() const noexcept {
        return _Last;
    }

    _NODISCARD constexpr size_t size() const noexcept {
        return static_cast<size_t>(_Last - _First);
    }

private:
    const _Elem* _First;
    const _Elem* _Last;
};

可以看到,其实initializer_list有两个指针,分别指向数据的开头和结尾,这意味着initializer_list指向的数据是连续存放的,但是我们使用时并传入的参数通常不会有着连续的地址。这就需要编译器就帮助了。

vector<int> a{1,2,3};
//等价于下面的语句
const int tmp[3] = {1, 2, 3};
vector<int> a(initializer_list{tmp, tmp + 3});

也就是说,编译器在编译时会增加显式分配一段连续空间的语句,并且以copy的形式,把数据传入这段地址。
一篇值得参考的文章:The cost of std::initializer_list
因此下面的两条语句,前者的执行速度会比后者慢得多(所以导致OJ超时了)。

max({a, b, c});
max(a, max(b, c));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值