我正在用 c++17 实现一个类,该类需要能够在编译时使用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
。
如果您不相信我,请考虑对结构进行类似的初始化:
struct A {int x; const char *y;}; A a = {1, "2"};
在这里,
{1, "2"}
不可能是std::initializer_list
,因为元素有不同的类型。
C++语法将这些大括号括起来的列表称为大括号初始化列表s。
std::initializer_list
是一个神奇的类(也就是说,在标准C++中无法实现(,可以从大括号的初始化列表构造。
正如您所注意到的,std::initializer_list
不能代替大括号的初始化列表。"大括号-init-list"是指特定的语法结构,因此数组的初始值设定项(例如在成员 init 列表中(必须是一个大括号括起来的列表。不能将此列表保存到变量中,并在以后使用它初始化数组。
constexpr A() : data{'1','2','3'} {} // Valid
constexpr A(initializer_list<char>/*or whatever*/ list) : data{list} {} // Not possible
一种可能的解决方案是使用循环将元素从std::initializer_list
复制到数组中。
由于构造函数是constexpr
的,数组必须用某些东西初始化;用零初始化它,使用: 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
数组与字符串文字 (char x[] = "123";
(。除了大括号的初始化列表之外,语法还允许在此处使用字符串文本。
它必须是字符串文字,即用引号括起来的字符串;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
将成为聚合(简单地说,一个没有自定义构造函数的结构,可以使用大括号的 init-list 按成员初始化(。然后两个A a{{'1','2','3'}};
、A a{'1','2','3'};
(可以省略大括号(和A a{"123"};
都可以。