为什么我不能用括号括起的初始化列表构造gsl :: span

use*_*895 8 c++ c++11 cpp-core-guidelines

根据C++核心指南,我应该使用gsl :: span来传递半开序列.

我认为这意味着不要像以下那样编写函数:

void func(const std::vector<int>& data) {
    for (auto v : data) std::cout << v << " ";
}
Run Code Online (Sandbox Code Playgroud)

我更喜欢:

void func(gsl::span<const int> data) {
    for (auto v : data) std::cout << v << " ";
}
Run Code Online (Sandbox Code Playgroud)

其优点在于它不会假定调用者将数据放入a中vector,或强制他们构造临时数据vector.他们可以通过一个std::array例子.

但一个常见的用例是传递一个括号括起来的初始化列表:

func({0,1,2,3})
Run Code Online (Sandbox Code Playgroud)

这适用于一个功能,std::vector但是为了一个功能,gsl::span我得到了错误信息:

错误C2664:'void func(gsl :: span)':无法将参数1从'initializer-list'转换为'gsl :: span'

它看起来像gsl::span 一个模板化的构造函数,旨在采取任何容器.

这只是Microsoft GSL实现中缺少的东西还是有充分理由阻止这种做法?

Mik*_*eMB 8

当您调用矢量版本时,初始化列表用于创建临时值std::vector,然后通过const引用传递给函数.这是可能的,因为std::vector有一个构造函数,它接受std::initializer_list<T>一个参数.
但是,gsl::span没有这样的构造函数,{1,2,3}并且没有类型,它也不能被你提到的模板化构造函数接受(除此之外,std::initializer_list<T>无论如何都不满足容器概念).

一个(丑陋的)解决方法当然是显式创建一个临时数组:

func(std::array<int,3>{ 0,1,2,3 });
Run Code Online (Sandbox Code Playgroud)

我没有看到一个特殊的原因,为什么gsl::span不应该有一个构造函数std::initializer_list,但请记住,这个库仍然是一个非常新的和正在积极开发.所以也许这是他们忽略的东西,没有时间实施,不确定如何正确地做或者确实有一些细节,这会使构造变得危险.最好直接在github上询问开发人员.


编辑:
正如@Nicol Bolas在他的评论中解释的那样,这是设计原因,因为像{0,1,2,3}(和其中的元素)这样的初始化列表是一个临时对象,并且gsl::span它本身不是一个容器(它不取得元素的所有权) ),他们认为偶然创建一个gsl::span包含对这些临时元素的悬空引用会很容易.

所以,虽然这样可以:

func({ 0,1,2,3 });
Run Code Online (Sandbox Code Playgroud)

因为初始化程序列表的生命周期在函数完成后结束,所以这样会创建一个悬空引用:

gsl::span<const int> data{ 0,1,2,3 };
func(data);
Run Code Online (Sandbox Code Playgroud)