当前位置:  开发笔记 > 编程语言 > 正文

为什么lambda不会从达到范围捕获类型const double,但const int是?

如何解决《为什么lambda不会从达到范围捕获类型constdouble,但constint是?》经验,为你挑选了2个好方法。

我似乎无法理解为什么下面的类型为const int的代码编译:

int main()
{
  using T = int;
  const T x = 1;
  auto lam = [] (T p) { return x+p; };
}
$ clang++ -c lambda1.cpp  -std=c++11
$

而这个类型为const double的那个不是:

int main()
{
  using T = double;
  const T x = 1.0;
  auto lam = [] (T p) { return x+p; };
}
$ clang++ -c lambda2.cpp  -std=c++11
lambda1.cpp:5:32: error: variable 'x' cannot be implicitly captured in a lambda with no capture-default specified
  auto lam = [] (T p) { return x+p; };
                               ^
lambda1.cpp:4:11: note: 'x' declared here
  const T x = 1.0;
          ^
lambda1.cpp:5:14: note: lambda expression begins here
  auto lam = [] (T p) { return x+p; };
             ^
1 error generated.

但是用constexpr double编译:

int main()
{
  using T = double;
  constexpr T x = 1.0;
  auto lam = [] (T p) { return x+p; };
}
$ clang++ -c lambda3.cpp  -std=c++11
$

为什么int的行为不同于double,或者除了int之外的任何其他类型,即int被const限定符接受,但double/other类型必须是constexpr?另外,为什么这段代码用C++ 11编译,我从[1]的理解是这种隐式捕获是C++ 14的特性.

.. [1] 这个带有空捕获列表的lambda如何能够引用到达范围名称?



1> Shafik Yaghm..:

这样做的原因是为了保持C++ 03兼容性,因为在C++ 03中使用常量表达式初始化的const整数或const枚举类型可用于常量表达式,但浮点数不是这种情况.

保留限制的基本原理可以在C++ 11之后的缺陷报告1826中找到(这解释了ABI中断评论)并询问(强调我的):

用常量初始化的const整数可以用在常量表达式中,但是用常量初始化的const浮点变量不能.这是故意的,与C++ 03兼容,同时鼓励constexpr的一致使用.然而,有些人发现这种区别令人惊讶.

还观察到允许const浮点变量作为常量表达式将是ABI突破性变化,因为它将影响λ捕获.

一种可能性是在常量表达式中不推荐使用const积分变量.

而回应是:

CWG认为当前的规则不应该改变,并且希望浮点值参与常量表达式的程序员应该使用constexpr而不是const.

我们可以注意到这个问题指出允许const浮点变量是常量表达式将是关于lambda捕获的ABI中断.

这是因为lambda不需要捕获变量,如果它没有使用odr并且允许const浮点变量是常量表达式将允许它们落在这个异常之下.

这是因为在常量表达式中允许使用常量表达式或constexpr文字类型初始化的const整数或枚举类型的左值到右值转换.对于使用常量表达式初始化的const浮点类型,不存在此类异常.这在C++ 11标准部分[expr.const] p2草案中有所介绍:

条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式[...]

并包含在[expr.const] p2.9中

除非适用,否则左值到右值的转换(4.1)

一个整数或枚举类型的glvalue,它引用一个带有前面初始化的非易失性const对象,用一个常量表达式初始化,或者

一个文字类型的glvalue,它指的是用constexpr定义的非易失性对象,或者指的是这样一个对象的子对象,或者

如果不再需要捕获非odr使用的const浮点值(这是ABI中断),则更改此值可能会影响lambda对象的大小.这个限制最初是为了保持C++ 03兼容性并鼓励使用constexpr,但现在这个限制已经到位,很难将其删除.

注意,在C++ 03中,我们只允许为const integral或const枚举类型指定一个类的常量初始化器.在C++ 11中,这被扩展了,我们被允许使用大括号或等号初始化器为constexpr文字类型指定常量初始化器.



2> 101010..:

根据标准§5.1.2/ p12 Lambda表达式[expr.prim.lambda](Emphasis Mine):

具有相关捕获默认值的lambda表达式(未明确捕获)this或具有自动存储持续时间的变量(这排除了任何已发现引用initcapture关联的非静态数据成员的id表达式),被认为是隐含的this如果复合语句捕获实体(即,变量):

(12.1) - 使用(3.2)实体,或

(12.2) - 在一个可能被评估的表达式(3.2)中命名实体,其中封闭的full-expression依赖于在lambda-expression的到达范围内声明的泛型lambda参数[示例:

void f(int, const int (&)[2] = {}) { } // #1
void f(const int&, const int (&)[1]) { } // #2
void test() {
const int x = 17;
auto g = [](auto a) {
f(x); // OK: calls #1, does not capture x
};
auto g2 = [=](auto a) {
int selector[sizeof(a) == 1 ? 1 : 2]{};
f(x, selector); // OK: is a dependent expression, so captures x
};
}

- 结束示例]所有这些隐式捕获的实体都应在lambda表达式的到达范围内声明.[注意:嵌套的lambda表达式对实体的隐式捕获可以通过包含lambda表达式来隐式捕获它(见下文).隐含的odr用法可能导致隐式捕获. - 结束说明]

这里的标准是,如果使用了odr,则需要捕获lambda中的变量.通过使用odr,标准意味着需要变量定义,因为它的地址被采用或者有对它的引用.

然而,这条规则有例外.其中一个特别感兴趣的是标准§3.2/ p3一个定义规则[basic.def.odr](Emphasis Mine):

变量x的名称显示为潜在评估的表达式ex,除非将lvalue-to-rvalue转换(4.1)应用于x,否则将生成一个不调用任何非平凡函数的常量表达式(5.20) x是一个对象,ex是表达式e的潜在结果集的一个元素,...

现在如果在例子中:

int main() {
  using T = int;
  const T x = 1;
  auto lam = [] (T p) { return x+p; };
}

int main() {
  using T = double;
  constexpr T x = 1.0;
  auto lam = [] (T p) { return x+p; };
}

将左值应用于右值转换,x我们得到一个常量表达式,因为在第一个例子中x是一个整数常量,在第二个例子中x是声明的constexpr.因此,x不需要在这些上下文中捕获.

但是,这不是示例的情况:

int main() {
  using T = double;
  const T x = 1.0;
  auto lam = [] (T p) { return x+p; };
}

在这个例子中,如果我们将左值应用于右值转换,x我们就不会得到常量表达式.

现在,你可能想知道为什么是这种情况,因为xconst double.那么答案是,如果一个变量constexpr是一个常量整数或一个枚举类型,那么声明的变量没有限定为常量表达式,并且在声明时用一个常量表达式初始化.这可以通过§5.20/ p2.7.1常量表达式[expr.const](Emphasis Mine)中的标准来证明:

条件表达式e是核心常量表达式,除非根据抽象机器(1.9)的规则评估e将评估以下表达式之一:

...

(2.7) - 左值 - 右值转换(4.1),除非适用于

(2.7.1) - 一个非整数或枚举类型的非易失性glvalue ,它引用一个完整的非易失性const对象,具有前面的初始化,用常量表达式初始化,......

因此,const double需要捕获变量,因为左值到右值的转换不会使常量表达式大喊大叫.因此,你理所当然地得到了编译错误.

推荐阅读
手机用户2402852307
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有