Pattern dependent search

It is known that a friend's function defined in the class scope can be found through an argument-dependent lookup, so we have to use the class type in the friend's function type, but if we define a friend's function outside the class, then the function parameters can be empty. So, how it works for template friends, if we have a specialization outside of the class, it should be visible as a normal friend function defined outside the 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 is goo visible and accessible but foo specialization for int not? VisualStudio 2012 gives error "foo": no candidate function available, "foo" [can be found via argument-dependent search] "By the way, GCC compiles the code without errors. Are there any restrictions in the standard, or is it just compiler problems ?

+3


source to share


1 answer


I think Klang might be right here. Consider two things:

ยง14.7.3 / 8 [temp.expl.spec], slightly shortening the example:

Explicit template specialization falls within 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 familiar function foo

,, lives according to ยง11.3 / 7 [class.friend] (emphasis mine):

A friend function defined in a class is within the (lexical) scope of the class in which it is defined.

To allow for specialization foo

, it has to be in the lexical area A

, which I think is not possible. Your specialization is foo<int>

on the wrong scale.

Note that this is only applicable if the function is defined in the class. The following compilations and works for me on Clang just fine because they are goo

no longer in scope 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
}

      



Visibility rules are set in section 7.3.1.2/3 (emphasis mine):

The declaration friend

does not by itself make the name unconditional search (3.4.1) or qualified search (3.4.3) visible. [Note. A friend's name will be visible in its namespace if a matching declaration is provided in the namespace scope (before or after the definition of a class that provides friendship). -end note]

So in this simpler example:

struct M {
    template <typename T> friend void foo(T ) { }
};

      

foo

is defined in M

, so it lives in the lexical area M

. There is no "comparable declaration" foo

outside M

, so it should only be visible with ADL (ยง3.4.2 / 4, emphasis mine):

When considering a linked namespace, searches are performed in the same way as searches performed when the associated namespace is used as a classifier (3.4.3.2), except that

(4.1) - Any using directives in the corresponding namespace are ignored.

(4.2). Any namespace or namespace-scope functions declared in associated classes are visible within their respective namespaces , even if they are not visible during normal lookups (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'll repeat my first sentence: I think Clang might be correct here.

+3


source







All Articles