It'n known that friend function defined in class scope can be found via argument-dependent lookup so we have to use class type in friend function type, but if we define friend function outside of class it function parameters can be left empty. So how this works for template friends, if we have specialization outside of class it should be visible as ordinary friend function defined outside of class scope.
#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
}
So why goo is visible and accessible but foo's specialization for int not? VisualStudio 2012 gives error "'foo': candidate function(s) not accessible, 'foo' [may be found via argument-dependent lookup]". By the way GCC compiles code without errors. Is there any restrictions in standard or this is just compilers issues?
I think Clang might be correct here. Consider two things:
§14.7.3/8 [temp.expl.spec], abridging the example somewhat:
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 ]
But, the friend function in question, foo
, lives in, as per §11.3/7 [class.friend] (emphasis mine):
A friend function defined in a class is in the (lexical) scope of the class in which it is defined.
To provide a specialization of foo
, it would have to be in A
's lexical scope - which I do not think is possible to do. Your foo<int>
specialization was in the wrong scope.
Note that this only applies if the function is defined in the class. The following compiles and runs for me on Clang just fine, because now goo
is not in M
's scope:
#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
}
The visibility rules are established in §7.3.1.2/3 (emphasis mine):
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 ]
Thus, in this simpler example:
struct M {
template <typename T> friend void foo(T ) { }
};
foo
is defined in M
, so it lives in its M
's lexical scope. There is no "matching declaration" of foo
outside of M
so it should only be visible with ADL (§3.4.2/4, emphasis mine):
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+)
}
So I restate my first sentence: I think Clang may be correct here.