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"}; 都可以。