如何使用初始化列表正确地初始化成员数组?

9

我有一个结构体,它持有一个数组,并且我想传递一个初始化列表到结构体的构造函数中并将其转发到这个数组。为了说明这一点,我尝试了以下代码:

#include <initializer_list>

struct Vector
{
    float v[3];

    Vector(std::initializer_list<float> values) : v{values} {}
};

int main()
{
    Vector v = {1, 2, 3};
}

导致错误的原因

error: cannot convert ‘std::initializer_list<float>’ to ‘float’ in initialization

我尝试使用圆括号代替花括号 对于变量v,但是这给出了以下错误:

error: incompatible types in assignment of ‘std::initializer_list<float>’ to ‘float [3]’

我的主要动机是为了避免clang生成的以下警告:

template <int N>
struct Vector
{
    float v[N];
};

template <>
struct Vector<2>
{
    float x, y;
};

int main()
{
    Vector<2> v2 = {1.0f, 2.0f};         // Yay, works
    Vector<3> v3 = {1.0f, 2.0f, 3.0f};   // Results in warning in clang++
    Vector<3> u3 = {{1.0f, 2.0f, 3.0f}}; // Must use two braces to avoid warning
    // If I could make Vector<N> take an initializer list in its constructor, I
    // could forward that on to the member array and avoid using the double braces
}

警告:建议在子对象的初始化周围加上花括号

我的问题是:如何使用初始化列表初始化成员数组?(即,如何使第一个代码工作?或者这不可能吗?


对于聚合体,您甚至不需要一个构造函数。请注意,initializer_list的大小是可变的。使用initializer-list进行初始化与列表初始化之间也有区别(这是一种语言结构,并可能导致使用initializer-list调用构造函数)。 - dyp
你实际上不能这样做,但是你可以使用std::array代替吗? - juanchopanza
他可以使用std :: array,但我认为这是关于语言而不是标准库的问题。并且,尊重任何人,告诉某人去阅读标准库就像告诉幼儿通过阅读莎士比亚来学习英语一样。因此,std :: array不是一个很好的答案。 - Samuel Danielson
4个回答

12

普通的 C 风格数组不可赋值。如果您改用 std::array,初始化将变得非常简单。

#include <array>

struct Vector
{
    std::array<float, 3> v;

    Vector(std::array<float, 3> const& values) 
    : v(values)
    {}
};

int main()
{
    Vector v{{1, 2, 3}};
}

推荐使用std::array,它允许更好的初始化语法。 - Zyx 2000

9
如果你的类是一个聚合体(aggregate),你可以使用这种语法。
template < std::size_t len >
struct Vector
{
    float elems[len];
};

Vector<3> v = {1.0f, 2.0f, 3.0f};

注意:这是由于花括号省略而可能实现的。不需要v = {{1,2,3}};,虽然这也是可行的。Clang发出此警告是因为双括号的语法更清晰且更少出错(一个用于初始化v,一个用于初始化子聚合体elems)。
如果您的类不是聚合体,则可以使用可变模板:
#include <array>
#include <cstddef>

template < std::size_t len >
struct Vector
{
    std::array < float, len > m;  // also works with raw array `float m[len];`

    template < typename... TT >
    Vector(TT... pp) : m{{pp...}}
    {}

    // better, using perfect forwarding:
    // template < typename... TT >
    // Vector(TT&&... pp) : m{{std::forward<TT>(pp)...}}
    // {}
};

Vector<3> a = {1.0f, 2.0f, 3.0f};

2
尝试使用 std::copy
Vector(std::initializer_list<float> values) 
{
    std::copy(values.begin(), values.end(), v);
}

如果列表比v的容量大怎么办? - Samuel Danielson

0

要初始化一个非静态成员数组,请使用聚合语法。也就是说,直接用大括号列表填充N个元素,其中N是数组中元素的数量。如果元素类型是默认可构造的,则可以使用少于N个元素;缺失的元素将被值初始化。

没有从std::initializer_list到数组的转换。即使您使用的源类型可以转换为数组引用(永远不是值),在成员初始化程序中也无法利用转换,因为数组不可分配。在这些情况下,您必须在构造函数体内使用类似于std::copy的东西。

对于多维数组,请注意,由于数组不可分配,因此您只能使用大括号列表表示子数组。还有一种大括号省略的选项;使用std::remove_all_extents<TheArrayType>::type项目的平面列表,编译器将做出适当处理。(当内部非数组类型是内置字符类型时,您有时可以使用带引号的字符串作为多维数组的次内部初始化程序。)


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