我用c++ 11编译了以下代码。这段代码是对实际用法的简化。
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); } // */
}
我的问题是:为什么它编译using namespace bar;
与不合格的名称查找(见行与/* OK
)?虽然它不会在使用限定名称查找(bar::foo<>()
)或没有using namespace
的限定名称查找的变体中编译(参见/* NOK
行)?
命名空间bar
包含了不同的模板函数foo
的声明。
一个是最后声明的template<int M, int N> int foo(S<M>);
,每个作为友元函数声明的S<M>
对应一个。
好友功能只能通过ADL找到。因此,当您有bar::foo<42>(s)
时,您只能调用自由函数(其中42
是M
的值,N
无法推断,因此无法编译)。
在c++ 20之前,foo<42>(s)
将被解释为foo < 42 > (s)
((foo < 42) > s
带有相等的括号),因为<
没有被解释为模板头,因为查找foo
没有找到模板。
当您添加using namespace bar;
时,查找foo
会找到模板函数,因此foo<42>(s)
被解析为模板函数调用。这最终会选择重载解析期间通过ADL找到的友元函数。
通过在全局命名空间中添加一个函数模板foo
来修复这个问题:
// Only for ADL purposes so `foo` is parsed as a template
template<typename> void foo() = delete;
int main() {
bar::S<> s;
return foo<42>(s) + foo<>(s);
}