C++:在 constexpr 构造函数中初始化成员数组
C++: Initialize a member array within a constexpr constructor
我正在用 c++17 实现一个 class,它需要能够在编译时使用 constexpr
构造函数构造一个对象。该对象有一个数组成员,我似乎无法通过参数对其进行初始化。
我一直在尝试为此使用 std::initializer_list
,大致如下:
#include <cstdint>
#include <initializer_list>
struct A {
char data[100];
constexpr A(std::initializer_list<char> s): data(s) {}
};
int main() {
constexpr A a{"abc"};
}
但是由于某些原因这不起作用。
clang 6.0
(也尝试了更高版本)告诉我我错了,尽管我按照它说的去做:
test_initializer_list.cpp:7:46: error: array initializer must be an initializer list or string literal
constexpr A(std::initializer_list<char> s): data(s) {}
(注意,如果我直接给它一个字符串字面值,比如data("abc")
,它会编译)
g++
(7.5.0) 说我出于某种原因不能使用初始化列表 - 但如果是这样,我可以使用什么?
test_initializer_list.cpp:7:52: error: incompatible types in assignment of 'std::initializer_list<char>' to 'char [100]'
constexpr A(std::initializer_list<char> s): data(s) {}
^
我做错了什么吗?
解释会很长,请耐心等待。
您不能用 std::initializer_list
.
初始化数组
当你写类似 int arr[] = {1,2,3}
的东西时,大括号中的部分不是 std::initializer_list
。
If you don't believe me, consider a similar initialization for a structure:
struct A {int x; const char *y;};
A a = {1, "2"};
Here, {1, "2"}
can't possibly be an std::initializer_list
, because the elements have different types.
C++ 语法调用那些用大括号括起来的列表 braced-init-lists。
std::initializer_list
是一个神奇的 class(无法在标准 C++ 中实现,也就是说) 可以从 花括号初始化列表构造。
正如您所注意到的,std::initializer_list
不能用来代替 braced-init-list。 "Braced-init-list" 指的是一个特定的语法结构,因此数组的初始化器(例如在成员初始化列表中)必须字面上 是 一个大括号括起来的列表。您不能将此列表保存到变量中,然后再用它初始化数组。
constexpr A() : data{'1','2','3'} {} // Valid
constexpr A(initializer_list<char>/*or whatever*/ list) : data{list} {} // Not possible
一种可能的解决方案是使用循环将元素从 std::initializer_list
复制到数组中。
由于构造函数是constexpr
,数组必须用something初始化;使用 : data{}
:
用零初始化它
constexpr A(std::initializer_list<char> s) : data{} {/*copy elements here*/}
现在 A a({'1','2','3'});
可以工作了。但是A a("123");
还是不会。
字符串文字 ("123"
) 既不是花括号初始化列表也不是 std::initializer_list
,无法转换为它们。
使用字符串文字 (char x[] = "123";
) 初始化 char
数组有一个特殊规则。除了花括号初始化列表之外,语法还允许在那里使用字符串文字。
必须是字符串文字,即用引号括起来的字符串; const char *
变量或 char
的另一个数组不会做。
如果你想让A a("123");
有效,构造函数的参数需要是一个const char *
(还有一些其他选项,但它们在这里帮助不大)。使用循环将指向的内存中的字符复制到数组中。不要忘记用零 (: data{}
) 初始化数组,因为你的构造函数是 constexpr
.
还有第三种选择:将char data[100]
替换为std::array
,并将构造函数的参数设为std::array
。与普通数组不同,它们可以被复制,所以 : data(list)
可以工作。 std::array
可以用大括号初始化列表 (A a({'1','2','3'});
) 和大括号括起来的字符串文字 (A a({"123"});
).
初始化
您还有另一个选择:删除构造函数。然后,您的 struct
将成为 聚合 (简单地说,一个没有自定义构造函数的结构,可以使用大括号初始化列表按成员方式初始化)。那么 A a{{'1','2','3'}};
、A a{'1','2','3'};
(大括号可以省略)和 A a{"123"};
都可以。
我正在用 c++17 实现一个 class,它需要能够在编译时使用 constexpr
构造函数构造一个对象。该对象有一个数组成员,我似乎无法通过参数对其进行初始化。
我一直在尝试为此使用 std::initializer_list
,大致如下:
#include <cstdint>
#include <initializer_list>
struct A {
char data[100];
constexpr A(std::initializer_list<char> s): data(s) {}
};
int main() {
constexpr A a{"abc"};
}
但是由于某些原因这不起作用。
clang 6.0
(也尝试了更高版本)告诉我我错了,尽管我按照它说的去做:
test_initializer_list.cpp:7:46: error: array initializer must be an initializer list or string literal
constexpr A(std::initializer_list<char> s): data(s) {}
(注意,如果我直接给它一个字符串字面值,比如data("abc")
,它会编译)
g++
(7.5.0) 说我出于某种原因不能使用初始化列表 - 但如果是这样,我可以使用什么?
test_initializer_list.cpp:7:52: error: incompatible types in assignment of 'std::initializer_list<char>' to 'char [100]'
constexpr A(std::initializer_list<char> s): data(s) {}
^
我做错了什么吗?
解释会很长,请耐心等待。
您不能用 std::initializer_list
.
当你写类似 int arr[] = {1,2,3}
的东西时,大括号中的部分不是 std::initializer_list
。
If you don't believe me, consider a similar initialization for a structure:
struct A {int x; const char *y;}; A a = {1, "2"};
Here,
{1, "2"}
can't possibly be anstd::initializer_list
, because the elements have different types.
C++ 语法调用那些用大括号括起来的列表 braced-init-lists。
std::initializer_list
是一个神奇的 class(无法在标准 C++ 中实现,也就是说) 可以从 花括号初始化列表构造。
正如您所注意到的,std::initializer_list
不能用来代替 braced-init-list。 "Braced-init-list" 指的是一个特定的语法结构,因此数组的初始化器(例如在成员初始化列表中)必须字面上 是 一个大括号括起来的列表。您不能将此列表保存到变量中,然后再用它初始化数组。
constexpr A() : data{'1','2','3'} {} // Valid
constexpr A(initializer_list<char>/*or whatever*/ list) : data{list} {} // Not possible
一种可能的解决方案是使用循环将元素从 std::initializer_list
复制到数组中。
由于构造函数是constexpr
,数组必须用something初始化;使用 : data{}
:
constexpr A(std::initializer_list<char> s) : data{} {/*copy elements here*/}
现在 A a({'1','2','3'});
可以工作了。但是A a("123");
还是不会。
字符串文字 ("123"
) 既不是花括号初始化列表也不是 std::initializer_list
,无法转换为它们。
使用字符串文字 (char x[] = "123";
) 初始化 char
数组有一个特殊规则。除了花括号初始化列表之外,语法还允许在那里使用字符串文字。
必须是字符串文字,即用引号括起来的字符串; const char *
变量或 char
的另一个数组不会做。
如果你想让A a("123");
有效,构造函数的参数需要是一个const char *
(还有一些其他选项,但它们在这里帮助不大)。使用循环将指向的内存中的字符复制到数组中。不要忘记用零 (: data{}
) 初始化数组,因为你的构造函数是 constexpr
.
还有第三种选择:将char data[100]
替换为std::array
,并将构造函数的参数设为std::array
。与普通数组不同,它们可以被复制,所以 : data(list)
可以工作。 std::array
可以用大括号初始化列表 (A a({'1','2','3'});
) 和大括号括起来的字符串文字 (A a({"123"});
).
您还有另一个选择:删除构造函数。然后,您的 struct
将成为 聚合 (简单地说,一个没有自定义构造函数的结构,可以使用大括号初始化列表按成员方式初始化)。那么 A a{{'1','2','3'}};
、A a{'1','2','3'};
(大括号可以省略)和 A a{"123"};
都可以。