MSVC泛型lambda调用方using namespace指令泄露到lambda体是bug吗?
问题复现
首先看基础代码示例:
#include <boost/hana/transform.hpp> #include <range/v3/view/transform.hpp> auto constexpr f = [](auto) { using namespace ranges::views; auto xxx = transform; }; void caller() { using boost::hana::transform; f(1); }
上述代码在GCC和MSVC编译器中均可正常编译,说明using boost::hana::transform;不会影响泛型lambda f内部的名称查找,lambda体内的xxx明确对应ranges::views::transform,不存在歧义。
若将caller函数内的using boost::hana::transform;替换为using namespace boost::hana;,MSVC会报错提示lambda体内的transform为歧义名称,GCC则可正常编译。
最小可复现代码如下:
#include <boost/hana/transform.hpp> #include <range/v3/view/transform.hpp> auto constexpr f = [](auto) { using namespace ranges::views; auto xxx = transform; }; void caller() { #if 1 using namespace boost::hana; #else using boost::hana::transform; #endif f(1); }
结论
这是MSVC的实现Bug,GCC的行为完全符合C++标准规定,属于MSVC的已知历史遗留问题。
产生原因
问题根因是两个编译器对C++模板两阶段名称查找规则的实现差异:
- 带
auto参数的泛型lambda本质是模板类的仿函数实现,其函数体属于模板依赖上下文,名称查找严格遵循两阶段规则:- 第一阶段在lambda定义点执行,查找所有非依赖名称,此时lambda体内的
using namespace ranges::views;可见,非限定查找transform可以直接匹配到ranges::views::transform。 - 第二阶段在模板实例化点(即
f(1)调用处)执行,仅会查找依赖于模板参数的名称,且仅考虑定义点可见的声明、以及实例化点关联命名空间内的声明。
- 第一阶段在lambda定义点执行,查找所有非依赖名称,此时lambda体内的
- 此处
transform作为无参数的标识符出现,不依赖任何模板参数,属于非依赖名称,按照规则应当在第一阶段就完成名称绑定,实例化点的任何新增声明都不应该影响查找结果。 caller函数内的using namespace boost::hana;属于实例化点的本地上下文声明,既不在lambda定义点可见,也不属于transform名称相关的关联命名空间,标准明确要求这类声明不参与模板的名称查找。- MSVC早年完全没有实现两阶段查找,所有名称都推迟到实例化点查找,近年虽然在逐步补全实现,但部分边界场景仍然存在偏差,才会错误地把
caller内引入的boost::hana::transform纳入查找范围,误报歧义。
内容的提问来源于stack exchange,提问作者Enlico
相关产品推荐
相关产品推荐

