朋友模板参数相关查找
friend template argument-dependent lookup
已知在 class 范围内定义的友元函数可以通过参数相关查找找到,所以我们必须在友元函数类型中使用 class 类型,但是如果我们定义友元函数在 class 之外,它的函数参数可以留空。
那么这对模板朋友是如何工作的,如果我们在 class 之外进行专门化,它应该像在 class 范围之外定义的普通朋友函数一样可见。
#include <iostream>
class A
{
public:
A()
: x(20)
{}
template <typename T>
friend void foo()
{
std::cout << "Primary Template" << std::endl;
}
friend void goo();
private:
int x;
};
void goo()
{
std::cout << "some goo" << std::endl;
}
template <>
void foo<int>()
{
std::cout << "specialization" << std::endl;
}
int main()
{
A a;
foo<int>(); // VS 2012 gives error C3767: 'foo': candidate function(s)
// not accessible
// 'foo' [may be found via argument-dependent lookup]
goo(); // OK
}
那么,为什么 goo 是可见和可访问的,而 foo 对 int 的专门化却不是? VisualStudio 2012 给出错误“'foo':候选函数不可访问,'foo' [可以通过参数相关查找找到]”。顺便说一句,GCC 编译代码没有错误。标准中有任何限制还是这只是编译器问题?
我认为 Clang 在这里可能是正确的。考虑两件事:
§14.7.3/8 [temp.expl.spec],稍微删减示例:
A template explicit specialization is in the scope of the namespace
in which the template was defined. [Example:
namespace N {
template<class T> class X { /* ... */ };
template<> class X<int> { /* ... */ }; // OK: specialization
// in same namespace
}
template <> class N::X<double> { /* ... */ }; // OK: specialization
// in enclosing namespace
—end example ]
但是,根据 §11.3/7 [class.friend](强调我的),所讨论的友元函数 foo
位于:
A friend function defined in a class is in the (lexical) scope of the
class in which it is defined.
要提供 foo
的专业化,它必须在 A
的词法范围内——我认为这是不可能的。您的 foo<int>
专业化范围错误。
请注意,这仅适用于 class 中定义的函数。以下代码在 Clang 上为我编译和运行很好,因为现在 goo
不在 M
的范围内:
#include <iostream>
struct M
{
template <typename T>
friend void goo(); // only declared, not defined
};
template <typename T>
void goo() {
std::cout << "full" << std::endl;
}
template <>
void goo<int>() {
std::cout << "explicit" << std::endl;
}
int main()
{
goo<int>(); // prints explicit
}
可见性规则在 §7.3.1.2/3 中建立(强调我的):
The friend
declaration does not by itself make the name visible to
unqualified lookup (3.4.1) or qualified lookup (3.4.3). [ Note: The name of the friend will be visible in its namespace if a matching declaration is provided at namespace scope (either before or after the class definition granting friendship). —end note ]
因此,在这个更简单的示例中:
struct M {
template <typename T> friend void foo(T ) { }
};
foo
是在 M
中定义的,因此它位于 M
的词法范围内。在 M
之外没有 foo
的 "matching declaration",所以它应该只在 ADL 中可见(§3.4.2/4,强调我的):
When considering an associated namespace, the lookup is the same as the lookup performed when the
associated namespace is used as a qualifier (3.4.3.2) except that
(4.1) - Any using-directives in the associated namespace are ignored.
(4.2) - Any namespace-scope friend functions or friend function templates declared in associated classes are visible within their respective namespaces even if they are not visible during an ordinary lookup (11.3).
int main() {
foo(M{}); // compiles correctly on both GCC and Clang
foo(0); // should fail. Clang complains about "use of undeclared identifier 'foo'"
// but GCC 4.9.2 allows it! (update: fixed in 5+)
}
所以我重申我的第一句话:我认为 Clang 在这里可能是正确的。
已知在 class 范围内定义的友元函数可以通过参数相关查找找到,所以我们必须在友元函数类型中使用 class 类型,但是如果我们定义友元函数在 class 之外,它的函数参数可以留空。 那么这对模板朋友是如何工作的,如果我们在 class 之外进行专门化,它应该像在 class 范围之外定义的普通朋友函数一样可见。
#include <iostream>
class A
{
public:
A()
: x(20)
{}
template <typename T>
friend void foo()
{
std::cout << "Primary Template" << std::endl;
}
friend void goo();
private:
int x;
};
void goo()
{
std::cout << "some goo" << std::endl;
}
template <>
void foo<int>()
{
std::cout << "specialization" << std::endl;
}
int main()
{
A a;
foo<int>(); // VS 2012 gives error C3767: 'foo': candidate function(s)
// not accessible
// 'foo' [may be found via argument-dependent lookup]
goo(); // OK
}
那么,为什么 goo 是可见和可访问的,而 foo 对 int 的专门化却不是? VisualStudio 2012 给出错误“'foo':候选函数不可访问,'foo' [可以通过参数相关查找找到]”。顺便说一句,GCC 编译代码没有错误。标准中有任何限制还是这只是编译器问题?
我认为 Clang 在这里可能是正确的。考虑两件事:
§14.7.3/8 [temp.expl.spec],稍微删减示例:
A template explicit specialization is in the scope of the namespace in which the template was defined. [Example:
namespace N { template<class T> class X { /* ... */ }; template<> class X<int> { /* ... */ }; // OK: specialization // in same namespace } template <> class N::X<double> { /* ... */ }; // OK: specialization // in enclosing namespace
—end example ]
但是,根据 §11.3/7 [class.friend](强调我的),所讨论的友元函数 foo
位于:
A friend function defined in a class is in the (lexical) scope of the class in which it is defined.
要提供 foo
的专业化,它必须在 A
的词法范围内——我认为这是不可能的。您的 foo<int>
专业化范围错误。
请注意,这仅适用于 class 中定义的函数。以下代码在 Clang 上为我编译和运行很好,因为现在 goo
不在 M
的范围内:
#include <iostream>
struct M
{
template <typename T>
friend void goo(); // only declared, not defined
};
template <typename T>
void goo() {
std::cout << "full" << std::endl;
}
template <>
void goo<int>() {
std::cout << "explicit" << std::endl;
}
int main()
{
goo<int>(); // prints explicit
}
可见性规则在 §7.3.1.2/3 中建立(强调我的):
The
friend
declaration does not by itself make the name visible to unqualified lookup (3.4.1) or qualified lookup (3.4.3). [ Note: The name of the friend will be visible in its namespace if a matching declaration is provided at namespace scope (either before or after the class definition granting friendship). —end note ]
因此,在这个更简单的示例中:
struct M {
template <typename T> friend void foo(T ) { }
};
foo
是在 M
中定义的,因此它位于 M
的词法范围内。在 M
之外没有 foo
的 "matching declaration",所以它应该只在 ADL 中可见(§3.4.2/4,强调我的):
When considering an associated namespace, the lookup is the same as the lookup performed when the associated namespace is used as a qualifier (3.4.3.2) except that
(4.1) - Any using-directives in the associated namespace are ignored.
(4.2) - Any namespace-scope friend functions or friend function templates declared in associated classes are visible within their respective namespaces even if they are not visible during an ordinary lookup (11.3).
int main() {
foo(M{}); // compiles correctly on both GCC and Clang
foo(0); // should fail. Clang complains about "use of undeclared identifier 'foo'"
// but GCC 4.9.2 allows it! (update: fixed in 5+)
}
所以我重申我的第一句话:我认为 Clang 在这里可能是正确的。