문제

명시적 반환 유형과 자동 후행 반환 유형 사이에 이상한 차이점이 있다는 것을 발견했습니다.

다음 코드에서는 이 유형의 객체 하나를 인수로 사용하는 정수 및 iter 함수를 템플릿으로 하는 구조체를 정의합니다.반환 유형은 템플릿 값을 감소시킨 후 자신을 호출한 결과에 따라 달라집니다.

인스턴스화 루프를 깨기 위해(또는 그렇게 생각했습니다) 비종속 유형을 반환하는 특수화를 제공합니다.

템플릿을 인스턴스화하는 장난감 메인이 있습니다.

다음은 약간의 코드입니다.

template<int i> struct Int {};

constexpr auto iter(Int<0>) -> Int<0>;

template<int i> constexpr auto iter(Int<i>) -> decltype(iter(Int<i-1>{}));

int main(){
  decltype(iter(Int<10>{})) a;
}

이 코드는 gcc 4.9와 clang 3.5 모두에서 작동하지 않습니다.둘 다 무한 인스턴스화를 트리거합니다(특수 기본 사례와 일치하지 않음).

rec.cpp:11:62: fatal error: recursive template instantiation exceeded maximum depth of 256
template<int i> constexpr auto iter(Int<i>) -> decltype(iter(Int<i-1>{}));

이제 C++14를 사용하면 decltype(auto) 그리고 우리는 똑같은 것을 반환하는 템플릿의 본문을 제공합니다.

template<int i> struct Int {};

constexpr auto iter(Int<0>) -> Int<0>;

template<int i>
constexpr auto iter(Int<i>) -> decltype(auto) {
  return iter(Int<i-1>{});
}

int main(){
  decltype(iter(Int<10>{})) a;
}

이제 이는 두 컴파일러 모두에서 작동하며 예상대로 작동합니다.

전문화를 표현하기 위해 다양한 방법을 시도하고 (위치에 주의하기 위해) 약간 이동했지만, 그래도 자살을 막지는 못했습니다 ;(

나는 또한 코드를 더 많이 뿌리려고 노력했습니다. decltype 그리고 declval, 하지만 C++11 구문이 작동하지 않는 것 같습니다.

이름 조회에 대한 두 구문의 차이점을 설명해 줄 수 있나요?

도움이 되었습니까?

해결책

이는 오버로드 해결, 템플릿 오버로드 해결, 템플릿 선언 인스턴스화 및 템플릿의 상대적 순서 때문입니다. 정의 인스턴스화.

먼저 C++11 사례를 살펴보겠습니다.컴파일러가 평가해야 하는 경우 decltype(iter(Int<0>{})), 이름에 대한 과부하 해결을 수행합니다. iter 인수 prvalue로 호출됨 Int<0>.템플릿이 오버로드 세트에 있으므로 14.8.3을 적용합니다. [온도.끝]:

1 - 함수 템플릿은 해당 이름의 (템플릿이 아닌) 함수 또는 동일한 이름의 (다른) 함수 템플릿에 의해 오버로드될 수 있습니다.해당 이름에 대한 호출이 (명시 적으로 또는 연산자 표기법을 사용하여) 작성되면 템플릿 인수 인수 (14.8.2) 및 명시 적 템플릿 인수 (14.3)의 템플릿 인수 값에 대해 템플릿 인수 값 (14.3)을 확인하는 경우 템플릿 인수 값을 찾습니다. 해당 함수 템플릿과 함께 사용할 수있는 경우 호출 인수로 호출 할 수있는 함수 템플릿 전문화를 인스턴스화 할 수 있습니다.[...]

그 결과 선언문은 template<int i> constexpr auto iter(...) -> ... 인스턴스화되었습니다(14.7.1p10 [임시.inst]) 와 함께 i = 0, 이는 다음의 평가를 강제합니다. decltype(iter(Int<-1>{})) 그리고 우리는 음의 정수라는 토끼굴을 따라갑니다.

그건 중요하지 않아 constexpr auto iter(Int<0>) -> Int<0> 더 나은 과부하가 될 것입니다(13.3.3p1 기준). [오버.매치.베스트]), 왜냐하면 우리는 그렇게까지 도달하지 못하기 때문입니다.컴파일러는 음의 무한대를 향해 즐겁게 행진하고 있습니다.

대조적으로 C++14에서는 반환 유형 7.1.6.4p12를 추론했습니다. [dcl.spec.auto] 적용됩니다:

12 - 정의가 인스턴스화될 때 선언된 유형에 자리 표시자가 있는 함수 템플릿에 대한 반환 유형 추론이 발생합니다. [...]

정의 인스턴스화가 발생하므로 ~ 후에 템플릿 오버로드 해결(14.7.1p3), 잘못된 템플릿 iter<0> 인스턴스화되지 않습니다.14.8.3p5:

5 - 후보 기능 세트에 전문화를 입력하려면 기능 템플릿 전문화의 서명만 필요합니다.따라서 템플릿 특수화가 후보인 호출을 해결하려면 함수 템플릿 선언만 필요합니다.

'서명'은 iter<0> 여기는 (Int<0>) -> decltype(auto), 다음을 포함하는 서명 자리 표시자 유형 (7.1.6.4).


제안된 해결 방법:SFINAE를 사용하여 호출 시도를 방지합니다. iter(Int<-1>{}):

template<int i> constexpr auto iter(Int<i>)
  -> decltype(iter(typename std::enable_if<i != 0, Int<i-1>>::type{}));
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        ^^^^^^^

SFINAE가 가야 한다는 점에 유의하세요. 내부에 그만큼 decltype, 그리고 실제로 호출 내부에서 iter.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top