为什么不能用括号括起来的初始化程序列表构造gsl :: span [英] Why can't I construct a gsl::span with a brace-enclosed initializer list
问题描述
根据 C ++核心指南,我应该使用gsl :: span传递半开序列.
我认为这意味着不要编写像这样的函数:
I think that means that instead of writing a function like:
void func(const std::vector<int>& data) {
for (auto v : data) std::cout << v << " ";
}
我应该喜欢:
void func(gsl::span<const int> data) {
for (auto v : data) std::cout << v << " ";
}
其优点在于,它不假定调用方将其数据存储在 vector
中,也不强制调用方构造临时的 vector
.例如,他们可以传递 std :: array
.
Which has the advantage that it does not assume the caller has their data in a vector
, or force them to construct a temporary vector
. They could pass a std::array
for example.
但是一个常见的用例是传递一个用大括号括起来的初始化器列表:
But a common use-case is to pass a brace-enclosed initializer list:
func({0,1,2,3})
这对于采用 std :: vector
的函数有效,但对于采用 gsl :: span
的函数,我会收到错误消息:
This works for a function taking a std::vector
but for a function taking a gsl::span
I get the error message:
错误C2664:'void func(gsl :: span)':无法转换从'initializer-list'到'gsl :: span'的参数1
error C2664: 'void func(gsl::span)' : cannot convert argument 1 from 'initializer-list' to 'gsl::span'
它看起来像 gsl :: span
有一个模板化的构造函数,用于接收任何容器.
It looks like gsl::span
has a templated constructor designed to take any container.
这只是Microsoft GSL实施中缺少的东西吗?还是有充分的理由阻止这种做法?
Is this just something missing from the Microsoft GSL implementation or is there a good reason to prevent this practice?
推荐答案
调用矢量版本时,初始化程序列表用于创建临时的 std :: vector
,然后将其传递给通过const引用的功能.这是可能的,因为 std :: vector
具有一个构造函数,该构造函数将 std :: initializer_list< T>
作为参数.但是, gsl :: span
没有这样的构造函数,并且 {0,1,2,3}
没有类型,它也不能被您提到的模板化构造函数接受(除了事实, std :: initializer_list< T>
仍然不能满足容器的概念.
When you call the vector version, the initializer list is used to create a temporary std::vector
, which is then passed to the function by const reference. This is possible, because std::vector
has a constructor, that takes an std::initializer_list<T>
as an argument.
However, gsl::span
doesn't have such a constructor and as {0,1,2,3}
doesn't have a type, it also can't be accepted by the templated constructor you mentioned (besides the fact, that std::initializer_list<T>
wouldn't satisfy the container concept anyway).
一个(丑陋的)解决方法当然是显式创建一个临时数组:
One (ugly) workaround would be of course to explicitly create a temporary array:
func(std::array<int,4>{ 0,1,2,3 });
我没有看到特定的原因,为什么 gsl :: span
不应具有采用 std :: initializer_list
的构造函数,但是请记住,该库仍处于新的阶段,并且正在积极开发中.因此,也许是他们忽略了这些东西,没有时间实施,不确定如何正确执行操作或确实存在一些细节,这会使该构造变得危险.最好直接在github上询问开发人员.
I don't see a particular reason, why gsl::span
should not have a constructor that takes a std::initializer_list
, but keep in mind that this library is in still pretty new and under active development. So maybe it is something they overlooked, didn't have time to implement, weren't sure how to do properly or there are really some details, that would make that construct dangerous. Its probably best to ask the developers directly on github.
正如@Nicol Bolas在他的评论中解释的那样,这是设计的原因,因为初始化列表类似于 {0,1,2,3}
(及其中的元素)是一个临时对象,并且 gsl :: span
本身并不是一个容器(它没有"),他们认为意外地创建一个 gsl :: span
,其中包含对这些临时元素的悬挂引用会很容易.
As @Nicol Bolas explains in his comment, this was by design because an initializer list like {0,1,2,3}
(and the elements within) is a temporary object and as gsl::span
is not a container in its own right (it doesn't take ownership of the elements), they think it would be too easy to accidentally create a gsl::span
that contains a dangling reference to those temporary elements.
所以,这没关系:
func({ 0,1,2,3 });
由于初始化器列表的生存期在函数完成后结束,因此将创建一个悬空的引用:
because the lifetime of the initializer list ends after the completion of the function, something like this would create a dangling reference:
gsl::span<const int> data{ 0,1,2,3 };
func(data);
这篇关于为什么不能用括号括起来的初始化程序列表构造gsl :: span的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!