用大括号括起来的初始化列表初始化 class

Initialize a class with brace-enclosed initializer list

我正在尝试用大括号括起来的初始化列表来初始化 class Vec,就像在这个最小的例子中那样调用:

int main()
{
    Vec<3> myVec1{{1,2,3}}; // or: myVec1({1,2,3});
    Vec<3> myVec2;
    myVec2 = {1, 2, 3};
    Vec<3> myVec3 = {1,2,3};
    return 0;
}

所有这些初始化(和赋值)都应该有效。 (加上自定义默认构造函数。因此不可能使用聚合 class。)

虽然我可以像这样使用 std::initializer_list:

template <unsigned C>
struct Vec
{
    Vec(){}
    Vec(std::initializer_list<int> list)
    {
        //does not work for obvious reasons:
        //static_assert(list.size() == C, "");
    }
};

我无法静态地确保参数的数量等于我的模板参数,即使用 {1, 2, 3, 4} 的初始化只会在运行时失败。

浏览 SO,我想到了以下内容:

template <unsigned C>
struct Vec
{
    Vec(){}
    Vec(const unsigned(&other)[C]){}
    Vec& operator=(const unsigned(&other)[C]){return *this;}
};

这对于赋值和 () 或 {} 初始化(对于 myVec1)工作正常 - 但它对于使用 = 的初始化(对于 myVec3)失败。

(GCC 给出错误 "could not convert '{1, 2, 3}' from '' to 'Vec<3u>'")

我不明白为什么其中一个初始化应该起作用,而另一个却不行。 关于如何使用大括号括起来的初始值设定项列表,同时确保编译时的正确长度,还有其他想法吗?

谢谢:)

初始化列表更适用于列表大小是动态的情况。在您的情况下, Vec 的大小是模板参数(即静态),您最好使用可变参数:

template <unsigned C>
struct Vec
{
    Vec(){}

    template<typename ... V>
    Vec(V ... args)
    {
        static_assert(sizeof...(V) == C, "");
        //...
    }
};

那么这些就可以了:

Vec<3> myVec2;
myVec2 = {1, 2, 3};
Vec<3> myVec3 = {1,2,3};

但是这个不会,因为它明确需要 std::initializer_list:

Vec<3> myVec1{{1,2,3}};

你需要两对括号:

Vec<3> myVec3 = {{1,2,3}};

当你这样做时:

Vec<3> myVec3 = {1, 2, 3};
// It is the same as (except that explicit constructor are not considered):
Vec<3> myVec3 = Vec<3>{1, 2, 3};'

所以基本上,编译器会为 Vec<3> 寻找一个构造函数,它要么接受一个 std::initializer_list,要么寻找一个接受 3 个参数的构造函数,所有这些参数都可以从 int 构造。

如果你想要一个类似于 std::array 的结构,你需要将你的类型设为 an aggregate,这意味着它不应该有用户定义的构造函数:

template <unsigned N>
struct Vec {
  int data[N];
};

void f() {
  Vec<3> v1{{1, 2, 3}};
  Vec<3> v2 = {{1, 2, 3}};
}