C ++ 사양의 어떤 부분이 관련 네임 스페이스 집합에서 함수 템플릿을 찾지 못하도록 인수 종속 조회를 제한합니까? 즉, main
아래 의 마지막 호출 이 컴파일에 실패하는 이유는 무엇입니까?
namespace ns {
struct foo {};
template<int i> void frob(foo const&) {}
void non_template(foo const&) {}
}
int main() {
ns::foo f;
non_template(f); // This is fine.
frob<0>(f); // This is not.
}
답변
이 부분에서 설명합니다.
C ++ 표준 03 14.8.1.6 :
[참고 : 간단한 함수 이름의 경우 함수 이름이 호출 범위 내에 표시되지 않는 경우에도 인수 종속 조회 (3.4.2)가 적용됩니다. 이는 호출이 여전히 함수 호출 (3.4.1)의 구문 형식을 가지고 있기 때문입니다. 그러나 명시 적 템플릿 인수가있는 함수 템플릿을 사용하는 경우 호출 지점에 해당 이름을 가진 함수 템플릿이 없으면 호출에 올바른 구문 형식이 없습니다. 이러한 이름이 표시되지 않으면 호출이 구문 상 제대로 구성되지 않은 것이며 인수 종속 조회가 적용되지 않습니다. 이러한 이름이 표시되면 인수 종속 조회가 적용되고 다른 네임 스페이스에서 추가 함수 템플릿을 찾을 수 있습니다.
namespace A {
struct B { };
template<int X> void f(B);
}
namespace C {
template<class T> void f(T t);
}
void g(A::B b) {
f<3>(b); //ill-formed: not a function call
A::f<3>(b); //well-formed
C::f<3>(b); //ill-formed; argument dependent lookup
// applies only to unqualified names
using C::f;
f<3>(b); //well-formed because C::f is visible; then
// A::f is found by argument dependent lookup
}
답변
C ++ 20부터 adl은 명시 적 함수 템플릿에서도 잘 작동합니다. 제안은 다음과 같습니다.
P0846R0 : 보이지 않는 ADL 및 기능 템플릿 :
사용자에게 템플릿 키워드를 사용하도록 요구하는 대신, 일반 조회가 결과를 생성하지 않거나 하나 이상의 함수를 찾고 뒤에 “<“가 오는 이름이 다음과 같이 처리되도록 조회 규칙에 대한 개정이 제안되었습니다. 기능 템플릿 이름이 발견되어 ADL이 수행되는 경우
현재 GCC 9 만이 기능을 구현하므로 예제를 컴파일 할 수 있습니다.
답변
약간 받아 들여진 답변을 수정하고 싶습니다. OP 질문에서는 명확하지 않지만 표준 (Kornel에서 인용)에서 중요한 부분은 다음과 같습니다 (강조 내) :
그러나 명시 적 템플릿 인수가 있는 함수 템플릿을 사용하면 호출에 올바른 구문 형식이 없습니다.
따라서 금지 된 것은 ADL에 의존하고 명시적인 템플릿 인수를 사용하는 것입니다. 불행히도 비 유형 템플릿 인수를 사용하려면 명시 적 인수를 사용해야합니다 (기본값이없는 경우).
다음은이를 보여주는 샘플 코드입니다. :
#include <string>
#include <utility>
namespace C {
struct B { };
template<class T> void f(T t){}
}
void g(C::B b) {
f(b); // OK
//f<C::B>(b); // ill-formed: not a function call, but only
// because explicit template argument were used
std::string s;
move(s); // OK
//move<std::string&>(s); // Error, again because
// explicit template argument were used
std::move<std::string&>(s); // Ok
}
int main()
{
C::B b;
g(b);
}
답변
편집 : 아니, 이것은 옳지 않습니다. @Kornel의 답변을 참조하십시오 .
확실하지는 않지만 Stroustrup의 “The C ++ programming language”를 참조하여 부록 C 섹션 13.8.4 가 원인 이 될 수 있다고 생각합니다 .
frob
은 템플릿 이므로 i=0
호출 한 후 특정 시점에 이를 전문화 할 수 있습니다. 즉 frob
, 인스턴스화 지점 이나 번역 단위 처리가 끝날 때 선택할 수있는 것처럼 보이는대로 호출 할 항목 을 선택하는 두 가지 가능한 방법이 구현에 남게됩니다 .
그래서 문제는 당신이 할 수 있다고 생각합니다
namespace ns {
struct foo {};
template<int i> void frob(foo const&) {}
}
int main() {
ns::foo f;
frob<0>(f);
return 0;
}
namespace ns {
template<> void frob< 0 >(foo const&) { /* Do something different*/ }
}