Bri*_*ian 10 c++ language-lawyer
假设一个非泛型 lambda 出现在模板化实体中,如下面的代码所示:
template <class T, auto F = [](T x) { *x; }>
void foo(T t) {}
void foo(...) {}
int main() {
foo(42);
}
Run Code Online (Sandbox Code Playgroud)
[expr.prim.lambda.closure]/2与此处相关:
闭包类型在包含相应lambda 表达式的最小块作用域、类作用域或命名空间作用域中声明。[...]
因此,在替换 into 期间foo<int>,在全局范围内声明了 lambda 闭包类型,并且其函数调用运算符不是模板。该 lambda 闭包类型是一个模板化实体 ( [temp.pre]/8.5 ),因此它的函数调用运算符 ([temp.pre]/8.3) 也是如此。但后者既不是函数模板专业化,也不是类模板专业化的成员,对于后者,将发生隐式实例化,如[temp.inst]/5中所述。
最直接的解读就是上面代码中的lambda函数调用操作符并不受按需实例化的影响;它只是与闭包类型同时定义。这似乎与 GCC 拒绝上述代码的情况一致。另一种可能的解释是,它的目的是像函数模板专门化一样根据需要进行实例化,并且措辞上存在疏忽;这将使上面的代码格式良好,可以被foo<int>调用。Clang 似乎不同意这两种解释。它调用foo(...),就好像另一个重载在直接上下文中发生替换失败一样(这对我来说似乎是错误的;lambda 表达式不在直接上下文中)。
经过深思熟虑,我认为GCC是正确的。默认情况下,模板化实体的实例化含义应被视为包括其所包含的任何实体的实例化,而不是在实例化最近的封闭实体后仍然依赖的实体。例如,在
template <class T>
T foo(T t) {
return t + 1;
}
Run Code Online (Sandbox Code Playgroud)
我们不需要标准中明确的规则来说明当foo实例化时,return其主体中的语句也被实例化。这是隐含的。
这也是为什么该标准仅仅有一个注释,指出本地类和本地类的成员不受它们所属范围的函数定义的单独实例化的影响。注释是非规范性的,因此此类注释仅用于指出规范性规则的结果。因为本地类的定义及其每个成员的定义在函数定义中按词法出现,所以在函数定义时,所有此类封闭的定义都已实例化,这已经是隐式的。
[temp.inst] 中列举了一般规则的各种例外情况;函数的默认参数不会作为它们在词法上出现的函数声明或定义的实例化的一部分进行实例化,例如 ( [temp.inst]/5 )。因为在封闭模板化实体中词汇上的lambda 表达式(或lambda 表达式的成员)不存在这样的例外,所以lambda 表达式及其闭包类型和闭包类型的定义operator()必须实例化为一部分最近的封闭模板化实体的实例化,此时lambda 表达式及其闭包类型不再依赖。如果它是通用 lambda,则它operator()是一个函数模板,并且其实例化被推迟到 [temp.inst]/5 需要时;如果它是非泛型 lambda,则它不再依赖并立即实例化。
| 归档时间: |
|
| 查看次数: |
212 次 |
| 最近记录: |