这与odr的使用有关。
首先,从
[basic.def.odr]/10
:
在以下情况下,本地实体在作用域中是odr可用的:
-
本地实体不是*this,或者存在一个封闭类或非lambda函数参数作用域,如果最内部的作用域是函数参数作用域,则它对应于一个非静态成员函数,以及
-
对于实体引入点和范围(其中*被认为是在最内层的封闭类或非lambda函数定义范围内引入)之间的每个中间范围([basic.scope.scope]),可以:
如果一个本地实体在一个不能使用odr的范围内使用odr,那么该程序的格式就不正确。
所以在这个例子中,
a
odr可用吗
b
不是:
void foo() {
constexpr const int b { 123 };
constexpr const auto l1 = [](int a) { return b * a; };
(void) l1;
}
在这个例子中,类似地
A.
和
c
odr是否可用,但两者都不可用
B
或者不
l1
是
void foo() {
constexpr const int b { 123 };
constexpr const auto l1 = [](int a) { return b * a; };
(void) [](int c) { return l1(c) * c; };
}
但这条规则不仅仅是“不能使用odr”,它也是“使用odr”。odr使用的是哪一种?那是
[basic.def.odr]/5
:
如果表达式是表示变量的id表达式,则该变量由表达式命名。变量x的名称显示为可能计算的表达式E,它是E使用的odr
-
x是在常量表达式([expr.const])中可用的引用,或
-
x是一个非引用类型的变量,可在常量表达式中使用,并且没有可变的子对象,E是应用左值到右值转换([conv.lval])的非易失性限定非类类型表达式的潜在结果集的一个元素,或者
-
x是非引用类型的变量,E是未应用左值到右值转换的废弃值表达式([expr.context])的潜在结果集的元素。
对于
b * a
案例
B
是“一个在常量表达式中可用的非引用类型的变量”,我们正在用它来应用“左值到右值对流”。这是规则的第二个例外,所以
B
是
不
使用odr,所以我们没有使用odr,但没有odr可用性问题。
对于
l1(c)
案例
l1
也是“在常量表达式中可用的非引用类型的变量”。。。但我们并没有对它进行左值到右值的转换。我们正在呼叫接线员。所以我们没有遇到异常,所以我们使用odr
l1
... 但它不能使用odr,这使得它的格式不正确。
这里的解决方案是
l1
(使其odr可用)或使其
static
或全球(使规则变得无关紧要)
l1
不再是本地实体)。