第一个友元函数模板声明可以通过' using namespace '看到,但不能通过限定名查找看到



我用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)时,您只能调用自由函数(其中42M的值,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);
}

相关内容

最新更新