Lambdas、本地类型和全局命名空间
Lambdas, local types, and global namespace
这个最小程序
template <typename X>
void foo (X x)
{
bar (x);
}
template <typename X>
void bar (X x)
{
}
int main ()
{
foo ([]{});
}
使用gcc(4.8.5和5.3)编译,但无法使用clang(3.7)编译
我的分析如下。
bar
在foo
中使用,在foo
之后声明,因此在foo
定义点不可见。在foo
实例化点可以找到bar
的唯一方法是通过依赖于参数的查找。
foo
和bar
的唯一自变量是在main
中定义的lambda。
显然,gcc将其类型视为在全局命名空间中声明的,而clang则不然。因此,gcc可以通过ADL找到bar
,而clang不能。
当我们使用main
:中本地定义的类型时,也会发生同样的情况
int main ()
{
struct K{};
foo (K()); // gcc compiles, clang complains
}
看起来gcc错了。根据标准,lambda的类型为未命名(expr.prim.lambda/3),因此它不应属于任何命名空间。本地类型也不应该属于全局命名空间。
分析正确吗?这是一个已知的gcc错误吗?
这个问题的灵感来源于这个问题。
GCC根据DR1690/1691的决议是正确的。
[expr.prim.lambda]/4:
闭包类型是在最小的块作用域、类作用域中声明的,或包含相应lambda表达式的命名空间范围。[注意:这确定了一组关联的命名空间和类具有闭包类型(〔basic.lookup.argdep〕)lambda声明符不会影响这些关联的命名空间和类。--尾注]
[basic.lookup.argdep]/2:
如果
T
是类类型(包括并集),则其关联类为:阶级本身;其所属类别(如有);及其直接和间接基类。其关联的命名空间是其关联类的最内部封闭命名空间。
所讨论的闭包类型的最里面的封闭命名空间是全局命名空间,因此全局命名空间是关联的命名空间。
GCC在这里是错误的。它通过ADL找到bar()
,即使[]{}
不是全局名称空间的成员。使用与T.C.相同的报价:
[expr.prim.lambda]/4:
闭包类型是在最小的块作用域、类作用域中声明的,或包含相应lambda表达式的命名空间范围。[注意:这确定了一组关联的命名空间和类具有闭包类型(〔basic.lookup.argdep〕)lambda声明符不会影响这些关联的命名空间和类。--尾注]
通过故意引入错误很容易看出这一点。在GCC中:
auto f = -[]{};
int main ()
{
foo (f);
}
error: no match for 'operator-' (operand type is '<lambda()>')
int main ()
{
foo (-[]{});
}
no match for 'operator-' (operand type is 'main()::<lambda()>')
另一方面,如果我们将lambda声明转移到全局范围,Clang不会抱怨:
auto f = []{};
int main ()
{
foo (f);
}
FWIW这被报道为GCC的Bug 57433,但尚未得到证实。它包含了更多GCC接受/Clang拒绝的程序示例。
- 在命名空间中使用全局命名空间中的函数
- 将好友从模板导出到全局命名空间
- Catalina C++:使用<cmath>标头产生错误:全局命名空间中没有名为 'signbit' 的成员
- 将以"_[a-z0-9]"开头和"using"的标识符导入全局命名空间是否定义良
- 为什么不允许在全局命名空间处阻止范围?
- 运算符<<依赖于参数的查找不在全局命名空间中查找
- 我可以停止 GCC 将标准库名称包含在全局命名空间中吗?
- "uintmax_t":不是"全局命名空间"的成员
- C++项目中的全局命名空间
- 使用 "::member" 引用全局命名空间有什么用吗?
- 在全局命名空间中重载不依赖于用户定义类型的标准定义类型的运算符是否格式正确?
- 使用全局命名空间限定符和指向数据成员的指针
- InitializeCriticalSectionEx 不是 atlwinverapi.h 中全局命名空间的成员
- C++在命名空间中声明与全局命名空间中的函数同名的函数
- 在vs2010中使用iostream时,出现错误C2039:'exit':不是"全局命名空间"的成员
- 是否可以恢复到"default"全局命名空间?
- C++错误代码全局命名空间
- equal()是否默认包含在全局命名空间中
- 前向声明和全局命名空间声明
- C++在全局命名空间中找不到非标准 C 函数