c++ lambda函数调用操作符何时示例化?

x33g5p2x  于 2023-04-01  发布在  其他
关注(0)|答案(1)|浏览(143)

假设一个非泛型lambda出现在一个模板化实体中,如下面的代码所示:

template <class T, auto F = [](T x) { *x; }>
void foo(T t) {}

void foo(...) {}

int main() {
    foo(42);
}

[expr.prim.lambda.closure]/2在这里是相关的:
闭包类型在最小的块作用域、类作用域或包含相应 lambda-expression 的命名空间作用域中声明。
因此,在替换到foo<int>的过程中,lambda闭包类型在全局范围内声明,并且它的函数调用操作符不是模板。([temp.pre]/8.5),因此它的函数调用操作符也是如此([temp.pre]/8.3)。但后者既不是函数模板特化,也不是类模板特化的成员如[temp.inst]/5中所描述的,将发生隐式示例化。
最直接的阅读是上面代码中的lambda函数调用操作符不受按需示例化的影响;它只是与闭包类型同时定义。这似乎与GCC拒绝上述代码一致。另一种可能的解释是,它旨在像函数模板专门化一样根据需要示例化,并且在措辞中存在疏忽;这将使上面的代码格式良好,并调用foo<int>。Clang似乎不同意这两种解释;它调用foo(...),就好像另一个重载在直接上下文中遇到了替换失败一样(我觉得这似乎是错的;lambda表达式不在直接上下文中)。

anauzrmj

anauzrmj1#

仔细考虑之后,我认为GCC是正确的。模板化实体的示例化的含义应该,默认情况下,被认为包括它所包含的任何实体的示例化,而不是在最近的封闭实体被示例化之后仍然依赖的实体。例如,在

template <class T>
T foo(T t) {
    return t + 1;
}

我们不需要在标准中明确规定当foo被示例化时,其主体中的return语句也被示例化。这是隐式的。
这也是为什么标准仅仅有一个注解,声明局部类和局部类的成员不受它们所属范围的函数定义的单独示例化的影响。注解是非规范性的,所以像这样的注解只是用来指出规范规则的结果。因为局部类的定义,以及它的每个成员的定义,如果在函数定义中词法地出现,则已经隐含的是,当函数定义为时,所有这样的封闭定义都被示例化。
在[temp.inst]中列举了一般规则的各种例外;函数的默认参数不作为它们在词法上出现的函数声明或定义的示例化的一部分来示例化,例如([temp.inst]/5)。因为对于 lambda-expression 没有这样的异常(或 lambda-expression 的成员),其在词法上位于封闭模板化实体内,* λ表达式 * 及其闭包类型的定义和闭包类型的operator()必须作为最近的封闭模板化实体的示例化的一部分被示例化,此时 lambda-expression 和它的闭包类型不再依赖。如果它是一个泛型lambda,它的operator()是一个函数模板,它的示例化被推迟到[temp.inst]/5需要时;如果它是一个非通用的lambda,那么它不再是依赖的,并立即被示例化。

相关问题