3

클래스 범위에 정의 된 친구 함수는 인수 종속 조회를 통해 찾을 수 있으므로 친구 함수 유형에는 클래스 유형을 사용해야하지만 클래스 외부에는 친구 함수를 정의하는 경우 함수 매개 변수는 비워 둘 수 있습니다. 템플릿 친구에게 이것이 어떻게 적용되는지, 클래스 외부의 특성화가있는 경우 클래스 범위 외부에서 정의 된 일반 친구 함수로 표시되어야합니다.친구 템플릿 인수 종속 조회

#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가 보이지 않고 액세스 할 수 있지만 int에 대한 foo의 특수화가 아닌 이유는 무엇입니까? VisualStudio 2012에서 " 'foo'오류가 발생했습니다 : 후보 함수를 액세스 할 수 없습니다. 'foo'[인수에 의존하는 조회를 통해 찾을 수 있습니다.] '. 그런데 GCC는 오류없이 코드를 컴파일합니다. 표준에 어떤 제한이 있습니까? 아니면 이것은 컴파일러의 문제입니까?

+0

잘 작동 gcc 4.9.2 http://ideone.com/iYvJFM – Ankur

+0

예 GCC가 오류없이 코드를 컴파일한다고 언급했습니다. –

+1

흥미 롭습니다. 이것에 3.5 pukes를 clang. 특수화 이전에 ' void foo();'템플릿을 추가하면 clang, btw에 대한 수정 사항이 수정됩니다. 'A'의 선언이 충분하지 않은 것 같습니다 (gcc의 경우 너무 놀랍습니다). – WhozCraig

답변

3

여기서 Clang이 정확하다고 생각합니다. 다소, 예를 abridging,

§14.7.3/8 temp.expl.spec] : 두 가지를 고려

템플릿 명시 전문화 템플릿시킨 스페이스 의 범위에 한정된. 실시 예 :

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 예]

그러나 질문 foo의 친구 함수 §11.3/7 class.friend (강조 당에 살고 광산) 클래스 정의

친구 기능은의 (사전) 범위에정의 된 클래스. 나는 생각하지 않는다 할 수 있습니다 -

foo의 전문성을 제공하기 위해, 그것은 A의 어휘 범위에있을 것이다. foo<int> 전문화 영역이 잘못되었습니다.

함수가 클래스에 정의 된 경우에만 적용된다는 점에 유의하십시오. 연타 잘에 나를 위해 다음 컴파일 및 실행, 지금 gooM의 범위에 포함되지 않기 때문에 :

#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 (강조 광산)에 설립 된 가시성 :

friend 선언 이 아니므로 정규화 된 조회 (3.4.1) 또는 정규 조회 (3.4.3)로 이름을 표시 할 수 없습니다. [참고 : 친구 이름이 인 경우 인 경우 일치하는 선언이 namespace 범위에 제공됩니다 (우정을 부여하는 클래스 정의 전후에 있음).-end 참고 이러한 단순한 예에 따라서

: 그것 M의 어휘 범위에 살고 있도록

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

fooM 정의된다. Mfoo 외부 아니오 "일치 선언"그렇게 만 ADL (§3.4.2/4, 강조 내)로 표시되어야 없다 :

연관된 네임 스페이스를 고려하여 조회와 동일 그

(4.1) 외에는 관련된 네임 스페이스를 규정 (3.4.3.2)로서 사용되는 경우 수행되는 검색 - 상관 사용-지시 연관된 네임 스페이스는 무시된다.

(4.2) - 그들은 일반 검색 (11.3) 동안 볼 수 없습니다 있는 경우 네임 스페이스 범위 내 친구 기능 또는 관련 클래스 에 선언 friend 함수 템플릿은 각각의 네임 스페이스 내에서 볼 수 있습니다.

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+) 
} 

그래서 나는 나의 첫 문장을 다시 설명 : 나는 연타 여기 옳은 것 같아요.

+0

그러나 [class.friend] 11.3/6은 "클래스가 클래스가 아닌 클래스 (9.8)이고, 함수 이름이 정규화되지 않은 경우에만 ** 함수를 정의 할 수 있으며, ** 함수는 네임 스페이스 범위 **를가집니다. 예 : 클래스 M { friend void f() {} // M의 친구 인 global f의 정의, // 멤버 함수 정의가 아님 } - 끝 예]]. 그래서 약간 혼란 스럽습니다 ** ** 11.3/7에서 어휘 범위 **를 의미합니다. 특수화는 기본 템플릿이 나타나는 범위를 포함해야합니다. 11.3/6 인라인 친구 정의에는 네임 스페이스 범위가 있습니다. –

+0

@VahagnBabajanyan 공개 규칙에 대한 관련 섹션을 찾았습니다. 희망적으로 그 점이 명확 해집니다. – Barry

+0

@VahagnBabajanyan [관련 질문] (http://stackoverflow.com/q/23171337/2069064). – Barry