用大括号括起来的初始值设定项列表初始化类

Initialize a class with brace-enclosed initializer list

本文关键字:初始化 列表 起来      更新时间:2023-10-16

我正试图用一个大括号括起来的初始值设定项列表初始化一个类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;
}

所有这些初始化(以及任务)都应该有效。(加上自定义默认构造函数。因此,使用聚合类是不可能的。)

而我可以使用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给出错误"无法将"{1,2,3}"从"转换为"Vec<3u>'")

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

感谢:)

Initializer列表更适合列表大小为动态的情况。在您的情况下,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的结构,你需要让你的类型成为一个聚合,这意味着它不应该有用户定义的构造函数:

template <unsigned N>
struct Vec {
  int data[N];
};
void f() {
  Vec<3> v1{{1, 2, 3}};
  Vec<3> v2 = {{1, 2, 3}};
}