【问题标题】:What's the correct way to initialize a member array with an initializer list?使用初始化列表初始化成员数组的正确方法是什么?
【发布时间】:2013-05-02 14:48:38
【问题描述】:

我有一个包含数组的struct,我想将初始化列表传递给结构的构造函数以转发到数组。为了说明,我尝试了:

#include <initializer_list>

struct Vector
{
    float v[3];

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

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

Which resulted in the error:

error: cannot convert ‘std::initializer_list&lt;float&gt;’ to ‘float’ in initialization

I tried using parentheses instead of braces for v 但这给出了错误:

error: incompatible types in assignment of ‘std::initializer_list&lt;float&gt;’ 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
}

warning: suggest braces around initialization of subobject

所以我的问题是:如何使用初始化列表初始化成员数组? (即我怎样才能使第一个代码工作? 还是不可能?

【问题讨论】:

  • 对于聚合,您甚至不需要 ctor。注意initializer_list 的大小是可变的。使用 initializer-list 进行初始化和 list-initialization 之间也有区别(这是一种语言结构,可能会导致使用初始化列表调用 ctor)。
  • 你真的做不到,但你能用std::array代替吗?
  • 他可以使用 std::array,但我将其视为关于语言的问题,而不是标准库。而且,不管是谁,告诉某人去阅读标准图书馆就像告诉一个蹒跚学步的孩子通过阅读莎士比亚来学习英语。所以 std::array 不是一个很好的答案。

标签: c++ c++11


【解决方案1】:

普通 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}};
}

【讨论】:

  • +1 推荐std::array。它允许更好的初始化语法。
【解决方案2】:

如果你的班级是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};

【讨论】:

    【解决方案3】:

    尝试使用std::copy

    Vector(std::initializer_list<float> values) 
    {
        std::copy(values.begin(), values.end(), v);
    }
    

    【讨论】:

    • 如果列表大于 v 的容量怎么办?
    【解决方案4】:

    要初始化数组非静态成员,请使用聚合语法。也就是说,直接用N 元素填充花括号列表,其中N 是数组中的元素数。如果元素类型是 Default-Constructible,则可以使用少于 N 的元素;缺少的元素将被初始化。

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

    注意多维数组,因为数组不是可赋值的,你只能使用花括号列表来表示子数组。还有大括号省略的选项;使用std::remove_all_extents&lt;TheArrayType&gt;::type 项目的平面列表,编译器将完成。 (当内部非数组类型是内置字符类型时,您有时可以使用带引号的字符串作为多维数组的倒数第二个初始值设定项。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-07
      • 2018-11-24
      • 2019-04-25
      • 1970-01-01
      • 2015-11-28
      • 2015-04-27
      • 1970-01-01
      • 2011-06-05
      相关资源
      最近更新 更多