使用const float*而不是const float**初始化的数组数组。

3

我看到了这段代码

float range[] = { 0, 256 };
const float* histRange = { range };

对我而言,{ range } 表示“数组的数组”,因为变量 range 是一个浮点数数组。因此,对我来说,这更有意义。

const float** histRange = { range };

但编译器(来自VS2019的VC ++)不能接受它(期望一个const float*)。

有人能解释一下为什么{range}不是'数组的数组'(float **),而只是普通的浮点指针吗?

有趣的事实:当我执行

float range[] = { 0, 256 };
auto aa = { range };
auto bb = range;
std::cout << typeid(aa).name() << std::endl;
std::cout << typeid(bb).name() << std::endl;

输出结果为(在VC++ VS2019中)。
std::initializer_list<float * __ptr64>
float * __ptr64

这个也不行:float* range = { 0, 256 };,但这个可以:const float* histRange[] = { range };。原因是{ 0, 256 }{ range }不是你可以指向的数组,它们是准备初始化数组的值列表。严谨地说,std::initializer_list<T>不能像真正的T[]那样衰减为T* - Ben Voigt
@BenVoigt 那看起来像是一个答案。 - cigien
@cigien:我期望一个恰当的回答能够涵盖如何使用列表初始化通过提供其构造函数所需的参数来初始化标量。 - Ben Voigt
@BenVoigt 是的,那肯定会让答案更好。如果您觉得评论不足以作为答案,那是合理的。 - cigien
@BenVoigt 严谨地说,一个带括号的列表并不是 std::initializer_list,除非在某些情况下,而这不是其中之一。 - M.M
1个回答

4

花括号括起来的文本不是表达式,也没有类型。花括号列表只允许在特定的上下文中使用,在每种情况下语言定义都指定了包含该花括号列表的更大上下文的语义。

在这一行中:

const float* histRange = { range };

我们正在初始化标量而不是数组。声明数组需要使用[]语法。它并没有定义一个无名数组,然后让histRange指向该数组的第一个元素。
用大括号列表初始化标量(除了auto外)的含义是该列表必须包含0或1个元素;如果包含一个元素,则行为与省略大括号相同。
换句话说,标量的初始化程序可以选择添加大括号,例如int x = { 5 };。自C以来,这个规则一直存在。
在此上下文中,不允许有超过一个元素的大括号列表(虽然gcc默认情况下接受它并发出警告,并忽略除第一个元素之外的任何列表元素)。
因此,const float* histRange = range;是允许的,因为range是一个数组,并且从数组到指向该数组第一个元素的指针之间存在隐式转换,其类型为float *,还有从float *const float *的隐式转换。
const float** histRange = range;将是错误的(带或不带可选大括号),因为没有从float *const float **的转换。
还允许const float *histArr [1] = {range};,这是一个指向常量浮点数的指针数组,长度为1;在这里,您可以具有大括号列表和更长的长度。
auto aa = {range};中,使用auto有其自己的类型推断规则,规则是该语法(在声明符中没有*[])将aa推导为具有一个元素的std :: initializer_list <T>类型。
请注意,仍然不正确地说{range}具有类型。 std :: initializer_list<T>由大括号列表的元素初始化。
这个规则稍有争议;在C ++ 11中,auto aa{range};是相同的,但是在C ++ 17中,它被改为推断aa具有与range相同的类型。

谢谢,这是一个非常清晰的解释。 - zeellos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接