当前位置: 首页 > 知识库问答 >
问题:

带有std::initializer_list的奇怪行为constexpr

公良飞尘
2023-03-14

我试图理解编译器在这里抱怨的原因:

// cexpr_test.cpp
#include <initializer_list>

constexpr int test_cexpr(std::initializer_list<const char*> x)
{
    return (int) (*x.begin())[0]; // ensuring the value isn't optimized out.
}

int main()
{
    constexpr int r1 = test_cexpr({ "why does this work," });

    constexpr std::initializer_list<const char*> broken { "but this doesn't?" };
    constexpr int r2 = test_cexpr(broken);

    return r1 + r2;
}

使用编译时生成的消息

g++ -std=c++11 -Wall -Werror cexpr_test.cpp 

如下:

cexpr_test.cpp:在函数 'int main()' 中: cexpr_test.cpp:12:76: error: 'const std::initializer_list{((const char* const*)(

令人困惑的是,为什么它会毫无问题地构造第一个初始值设定项列表。我在这里缺少什么?

共有1个答案

公孙胡媚
2023-03-14

问题在于此处损坏本身的初始化。什么是 std::initializer_list,它有什么意义?它是一个引用类型(即以某种方式引用另一个对象),它由 c 样式数组支持。此 c 样式数组的属性决定了initializer_list是否可以是 constexpr 变量。我们可以查阅 [dcl.init.list] 以获取这些属性。

5一个对象类型std::initializer_列表

struct X {
  X(std::initializer_list<double> v);
};
X x{ 1,2,3 };

初始化的实现方式大致如下:

const double __a[3] = {double{1}, double{2}, double{3}};
X x(std::initializer_list<double>(__a, __a+3));

假设实现可以用一对指针构造initializer_list对象。 — 结束示例 ]

6数组与任何其他临时对象具有相同的生命周期,除了从数组初始化initializer_列表对象会像绑定对临时对象的引用一样扩展数组的生命周期。[示例:

typedef std::complex<double> cmplx;
std::vector<cmplx> v1 = { 1, 2, 3 };

void f() {
  std::vector<cmplx> v2{ 1, 2, 3 };
  std::initializer_list<int> i3 = { 1, 2, 3 };
}

struct A {
  std::initializer_list<int> i4;
  A() : i4{ 1, 2, 3 } {}  // ill-formed, would create a dangling reference
};

对于 v1v2initializer_list 对象是函数调用中的参数,因此为 { 1, 2, 3 } 创建的数组具有完整表达式生存期。对于 i3initializer_list 对象是一个变量,因此数组在变量的生存期内保持不变。对于 i4initializer_list对象在构造函数的 ctor-initializer 中初始化,就像通过将临时数组绑定到引用成员一样,因此程序格式不正确 ([class.base.init])。 — 结束示例 ] [ 注意:如果可以这样分配具有相同初始值设定项的显式数组,则实现可以自由地在只读内存中分配数组。 — 尾注 ]

因此,此数组类似于常量引用引用的任何其他临时对象。这意味着我们实际上可以将您的最小示例减少到更小的示例

constexpr int test_cexpr(int const & x)
{
    return x; 
}

int main()
{
    constexpr int r1 = test_cexpr(0);

    constexpr int const &broken = 0;
    constexpr int r2 = test_cexpr(broken);

    return r1 + r2;
}

这会产生完全相同的行为和错误。我们可以直接将 0 作为参数传递给 constexpr 函数,引用绑定,我们甚至可以在函数内部引用它。但是,constexpr 引用不能使用 0 初始化。零不是有效的初始值设定项的原因是它需要具体化一个临时的 int 对象。此临时变量不是静态变量,因此不能用于初始化 constexpr 引用。就这么简单。

同样的道理也适用于你的案例。物化的临时数组不是具有静态存储持续时间的对象,因此不能用于初始化constexpr引用类型。

当直接将参数传递给<code>test_cexpr</code>时,它之所以有效,是因为相应的参数本身不是constexpr变量。这意味着它可以成功绑定。之后,它所绑定的对象必须在常量表达式中可用。不必对此进行过多的详细说明:因为在这种情况下,临时表达式具有完整的表达式生存期(而不是延长的生存期),所以它可以在常量表达式中使用。

 类似资料:
  • 问题内容: 给定一个数组,返回最后一个对象。例如: 在研究此问题时,除了这篇看似无关的SO帖子,我无法找到其他东西,这使我发现这是numpy中的一个公开错误(截至2015年3月):第一个报告和更相关的问题。发布此信息,以便其他人对此有所了解,可以更有效地找到此信息。 问题答案: 在版本,并表现为经典短路逻辑和/或功能。想到LISP行为。Python和运算符可以做到这一点。 一些例子: 返回逻辑上为

  • 我使用这个类在ImageView上绘制TriangleShapeView,它在用户单击事件时改变它的颜色和可绘制的图像。 在RecyclerView onBindViewHolder方法中,我检查,然后相应地设置TriangleShapeView颜色和可绘制图像: 在setOnClickListener中: 当项目第一次加载时,这可以正常工作,但当用户单击时: -第一个 我使用下面的xml布局来扩

  • 我有以下代码来解析一个JSON文件: 要处理以下JSON文件: 如果我执行此代码,我将收到以下错误: 所以我开始一步一步地调试应用程序,看看part processing()中的哪个代码部分抛出了这个异常。令人惊讶的是,那里的所有代码都正常执行:没有抛出异常,也没有返回结果I except。 更让我惊讶的是,当我稍微改变第一种方法的代码时,它可以在不产生异常的情况下工作。 我不知道println方

  • 问题内容: 我在GregorianCalendar类中遇到一个奇怪的行为,我想知道我是否真的做得不好。 仅当初始化日期的月份的实际Maximum大于我将日历设置为的月份时,才追加此值。 这是示例代码: 我知道问题是由于日历初始化日期是31天(可能是5月),与设置为2月(28天)的月份混淆了。修复很容易(只需在设置年和月之前将day_of_month设置为1),但是我想知道这确实是想要的行为。有什么

  • 问题内容: 我正在为一个问题而苦苦挣扎,我不明白为什么它不起作用。如何通过将变量传递并转换为? 为什么在顶部代码段中不起作用,但在行下方的底部代码段中起作用? 唯一的区别似乎是添加了一个额外的变量,该变量也被键入为? 问题答案: 该是一种原始类型,同时是一个普通的Java类。您不能在原始类型上调用方法。但是该方法在上可用,如javadoc中所示 有关这些原始类型的更多信息,请参见此处

  • 问题内容: 为什么的到哪里去了? 问题答案: 删除任何字符,并从字符串的开头和结尾。