【问题标题】:C++: Initialize a member array within a constexpr constructorC++:在 constexpr 构造函数中初始化成员数组
【发布时间】:2020-09-30 20:00:46
【问题描述】:

我正在 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) {}
                                                    ^

我做错了吗?

【问题讨论】:

  • 是什么让你认为"abc" 是(或可以转换成)std::initializer_list&lt;char&gt;?
  • @dfri - 谢谢,但不完全是,因为他们不需要 constexpr @Evg - 原则上你是对的,但试图把 {'a', 'b', 'c'} 放在那里也不起作用。

标签: c++ constexpr initializer-list


【解决方案1】:

解释会很长,但请耐心等待。


您不能使用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++ 语法将这些大括号括起来的列表称为braced-init-lists。

std::initializer_list 是一个神奇的类(即在标准 C++ 中无法实现),可以从一个花括号初始化列表构造。

如您所见,std::initializer_list 不能用于代替花括号初始化列表。 “Braced-init-list”指的是特定的语法结构,因此数组的初始化器(例如在成员 init 列表中)必须在字面上 be 一个大括号括起来的列表。你不能把这个列表保存到一个变量中,然后用它初始化一个数组。

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 数组有一个特殊规则。除了花括号初始化列表之外,该语法还允许在其中使用字符串文字。

必须是字符串literal,即用引号括起来的字符串; 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"};都可以工作。

【讨论】:

  • 感谢您的解释!我想我对在函数体解决方案中进行复制有一种感觉(尽管对于 constexpr 这更痛苦),但不知道 braced-init-list 与 initialization-list 不一样,这令人沮丧。也感谢 std::array 的想法!
猜你喜欢
  • 2011-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-26
  • 1970-01-01
相关资源
最近更新 更多