为何C++中using namespace bar的非限定查找可编译友元函数模板?
关于C++友元模板查找的编译问题解析
namespace bar { template<int M = 10> struct S { template<int N = M*2> friend int foo(S) { return N-M; } }; template<int M, int N> int foo(S<M>); } /* end of bar */ int main() { bar::S<> s; /* OK { using namespace bar; return foo<42>(s) + foo<>(s); } // */ /* NOK { return foo<42>(s) + foo<>(s); } // */ /* NOK { using namespace bar; return bar::foo<42>(s) + bar::foo<>(s); } // */ /* NOK */{ return bar::foo<42>(s) + bar::foo<>(s); } // */ }
这段C11代码中,仅当通过using namespace bar;引入命名空间后,以非限定名称调用foo<42>(s)和foo<>(s)能正常编译;而未引入命名空间的非限定调用、带bar::前缀的限定调用均编译失败,原因可从C的名称查找规则和友元特性两方面分析:
1. 友元函数模板的作用域特性
在bar::S内部定义的友元函数模板foo,其声明仅在S的类作用域内可见,不会自动注入到外围的bar命名空间作用域。而bar命名空间中单独声明的template<int M, int N> int foo(S<M>);是一个完全独立的双参数函数模板,和友元版本的foo(单参数模板)并非同一实体。
2. 非限定查找+using namespace bar的成功逻辑
当执行foo<42>(s)这类非限定调用时:
using namespace bar让bar命名空间内的所有名字进入当前作用域;- 由于参数
s是bar::S<>类型,编译器会触发参数依赖查找(ADL):自动去参数类型所在的bar命名空间中查找匹配的函数; - ADL过程中,编译器会考虑
S内部声明的友元函数模板foo——即使它没有在bar命名空间中显式暴露。此时foo<42>(s)会匹配友元的单参数模板(指定N=42),foo<>(s)会使用默认参数N=M*2(M=10,故N=20),两者均能正常实例化。
3. 未引入命名空间的非限定调用失败原因
未使用using namespace bar时:
- 全局作用域中没有
foo的声明; - ADL虽然会去
bar命名空间查找,但只能找到那个双参数的foo模板,而我们调用的foo<42>(s)只提供了一个模板参数,与双参数模板的签名不匹配,因此编译报错。
4. 限定名称bar::foo调用失败原因
当使用bar::foo这类限定查找时,编译器只会在bar命名空间的直接作用域中查找显式声明的名字,也就是那个双参数的foo模板,不会深入到S的类作用域中查找友元声明:
bar::foo<42>(s)仅提供了一个模板参数,而双参数模板需要两个参数且无默认值,匹配失败;bar::foo<>(s)同样缺少第二个模板参数,无法匹配双参数模板,因此编译报错。
内容的提问来源于stack exchange,提问作者user5534993
相关产品推荐
相关产品推荐

