2013-03-25 2 views
2

VS2010에 이상한 문제가 있습니다. 다음 코드는 (perfectly accepted by gcc을하면서) 컴파일에 실패 : 다음 오류로템플리트 반복 유형 오류

// Instantiate each type of a type array (which is a template<int>) 
// void is taken as the end of the array 
template<template<int> class A, int n = 0, typename obj_type = typename A<n>::type> 
struct Instantiate : public Instantiate<A,n+1> 
{ 
    obj_type object; 

    Instantiate() : object() {} 
}; 

template<template<int> class A, int n> 
struct Instantiate<A,n,void> 
{ 
}; 

// Does not compile 
template<typename O> 
struct test 
{ 
    template<int n, bool=true> struct array { typedef void type; }; 
    template<int n> struct array2 { typedef typename array<n>::type type; }; 

    template<bool u> struct array<0,u> { typedef int type; }; 
    template<bool u> struct array<1,u> { typedef float type; }; 
    template<bool u> struct array<2,u> { typedef bool type; }; 

    Instantiate<array2> objects; 
}; 

int main() 
{ 
    test<int> obj; 
} 

:

C1202: recursive type or function dependency context too complex 
see instanciation of class template 'Instantiate<A,n>' 
with 
[ 
    A=test<O>::array2, 
    n=1 
] 
(and so on, up to n=497...) 

을 오류를 해결합니다 (즉, juste 라인 template<typename O> 제거) test 비 템플릿을 만들기. 그러나이 간단한 예제에서도 가능하지만 내 코드에서는 더 이상 복잡하지 않습니다.

질문은 다음과 같습니다, VS 또는 GCC 권리

  • ? 아니면 VS 버그입니까?
  • VS가 잘못된 경우 해결 방법이 있습니까?

편집 : 은 - 제리의 대답에서, 컴파일러의 버그가 보인다 ... 그리고 그것은 VS2012에 고정되어있다. - 생성 된 클래스 계층은 다음과 같아야합니다

Instantiate<array2, 3, void> // end of recursion 
    ^
     | 
Instantiate<array2, 2, bool> 
    ^
     | 
Instantiate<array2, 1, float> 
    ^
     | 
Instantiate<array2, 0, int> 
+0

유용 할 수 있습니다. http://stackoverflow.com/questions/4357854/c-template-compilation-error-recursive-type-or-function-dependency –

+0

@Shafik 저는 이미 그것을 보았습니다. 그 오류. 어쨌든 고맙습니다. – Synxis

+0

배열 <2,u> 전문화 아래에서 array2 템플릿을 이동하려 했습니까? 차이가 있습니까? – Tomek

답변

0

는 rise4fun과 제리의 해결 덤비는 동안, 나는 발견 내부 Instantiate의 전체 정의를 넣어test'solved' the problem (이 VS2012RTM 대 VS2012CTP에 대한 자세한 내용은 제리의 답변을 참조)

template<typename O> 
struct test 
{ 
    template<template<int> class A, int n = 0, typename obj_type = typename A<n>::type> 
    struct Instantiate : public Instantiate<A,n+1> 
    { 
     obj_type object; 
     Instantiate() : object() {} 
    }; 

    template<template<int> class A, int n> 
    struct Instantiate<A,n,void> 
    { 
    }; 

    template<int n, bool=true> struct array { typedef void type; }; 
    template<int n> struct array2 { typedef typename array<n>::type type; }; 

    template<bool u> struct array<0,u> { typedef int type; }; 
    template<bool u> struct array<1,u> { typedef float type; }; 
    template<bool u> struct array<2,u> { typedef bool type; }; 

    Instantiate<array2> objects; 
}; 

int main() 
{ 
    test<int> obj; 
} 

사용자가 이미 사용한 매크로에 포함될 수 있으므로 사용 사례에 가장 적합합니다.

+0

라이브러리 케이스에서 효과가있는 솔루션을 발견해 주어서 다행입니다. 호기심에서 공개적으로 사용할 수 있습니까? – jerry

+0

예. [SPARK Particle Engine] (http://spark.developpez.com/index.php?lang=en)의 일부가 될 것입니다 (홈 페이지가 도청되면 포럼과 소스에 대한 자세한 정보를 찾을 수 있습니다) . – Synxis

0

그것은 컴파일러 구성의 버그 아니다. 컴파일 타임 스택은 런타임 스택처럼 제한됩니다. IIRC의 VS에서 컴파일 타임 스택의 최대 깊이는 512입니다. 문서에서 찾아 볼 가치가 있을지도 모르지만 확실하지는 않습니다.

+0

gcc와 동일합니다. 더욱이, 타입 배열은 3 개의 원소만을 갖는다. 따라서'void ='전문화에 해당하는'n == 3 '에서 재귀를 중지해야합니다. GCC는 그렇게합니다. – Synxis

1

VS 2010 (Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86)에서 이것을 재현 할 수 있으며 버그라고 생각합니다. 진실은, 여기에 관련된 범위 문제를 내 머리를 감싸는 힘든 시간을 보내고있다,하지만 그게 바로 옆에있는 문제에 아무런 영향을 미치지 않는 것 같다 (기본 템플릿과 전문화가 함께 중첩되어 있고 VS가 재귀 수준에 대해 불평하고있다. , 정의되지 않은 기호가 아님). 어쨌든 매우 흥미로운 예입니다. 또한 VS 2005 (Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86)에서 동일한 동작이 발생합니다. Microsoft가 VS 2010 (버그 번호 : Microsoft Connect Visual Studio pagefeedback form으로 판단)에서 여전히 버그 보고서를 수락하지 않는 것 같습니다. 문제가 아직 해결되지 않은 경우 VS 2012에 대한 액세스 권한이있는 사람이 원본 코드를 테스트하고 버그 보고서를 제출할 수 있기를 바랍니다. (이전에 Microsoft의 관심을 끌지 못했음)

이 문제를 해결하려면, 앞으로 재귀의 원인 클래스 템플릿 선언

// Forward declaration of problematic inherited class template 
template<template<int> class A, int n = 0, typename obj_type = typename A<n>::type> 
struct Instantiate; 

template<template<int> class A, int n> 
struct Instantiate<A,n,void> 
{ 
}; 

// Now compiles! 
template<typename O> 
struct test 
{ 
    template<int n, bool=true> struct array { typedef void type; }; 
    template<int n> struct array2 { typedef typename array<n>::type type; }; 

    template<bool u> struct array<0,u> { typedef int type; }; 
    template<bool u> struct array<1,u> { typedef float type; }; 
    template<bool u> struct array<2,u> { typedef bool type; }; 

    Instantiate<array2> objects; 
}; 

// Instantiate each type of a type array (which is a template<int>) 
// void is taken as the end of the array 
template<template<int> class A, int n, typename obj_type> 
struct Instantiate : public Instantiate<A,n+1> 
{ 
    obj_type object; 

    Instantiate() : object() {} 
}; 

int main() 
{ 
    test<int> obj; 
    return 0; 
} 

편집을

의 Synxis의 rise4fun 링크를 주셔서 감사합니다, 그것은 나중에 편리하게 할 수 있는지의 . 놀라운 점은 원래 코드가 VS2012 CTP에서 정상적으로 작동하지만 여전히 VS2012 RTM에서 오류가 발생했다는 것입니다. http://rise4fun.com/Vcpp/aRh을 참조하십시오. 왜 이 RTM이 아닌 기본 옵션이되는 이유는 무엇입니까? 이 예제는 확실히 이상한 것입니다.

test에 의해 사용되는 라이브러리 헤더에 Instantiate 이외의 것이 있습니까? 그렇지 않은 경우 사용자는 명시 적으로 Instantiate 템플릿을 선언하여 test을 정의한 다음 헤더를 포함시킬 수 있습니다.그렇지 않으면 필요한 다른 것들과 함께 forward 선언이있는 헤더 파일을 만들 수 있습니다. 대부분이 문제의 영향을받는 사용자를 위해 특수한 해결 방법 헤더를 만들려고합니다. 이것은 확실히 상황이 매우 혼란과 오류 (주로 인해 기본 템플릿 인수에) 경향이 있습니다,하지만 작동합니다

myLib_workaround_forward_declaration.hpp :

#ifndef MYLIB_WORKAROUND_FORWARD_DECLARATION // or #pragma once if supported 
#define MYLIB_WORKAROUND_FORWARD_DECLARATION 

// amount of error checking is up to you 
// eg, are workaround header files mixed with the regular ones 
// or included in the wrong order 

template<template<int> class A, int n = 0, typename obj_type = typename A<n>::type> 
struct Instantiate; 

#endif 

myLib_workaround_implementation.hpp :

#ifndef MYLIB_WORKAROUND_IMPLEMENTATION // or #pragma once if supported 
#define MYLIB_WORKAROUND_IMPLEMENTATION 

// amount of error checking is up to you 
// eg, are workaround header files mixed with the regular ones 
// or included in the wrong order 

template<template<int> class A, int n, typename obj_type> 
struct Instantiate : public Instantiate<A,n+1> 
{ 
    obj_type object; 

    Instantiate() : object() {} 
}; 

template<template<int> class A, int n> 
struct Instantiate<A,n,void> 
{ 
}; 
#endif 

userFile.cpp :

// Forward declaration of problematic inherited class template 
#include "myLib_workaround_forward_declaration.hpp" 

// Now compiles! 
template<typename O> 
struct test 
{ 
    template<int n, bool=true> struct array { typedef void type; }; 
    template<int n> struct array2 { typedef typename array<n>::type type; }; 

    template<bool u> struct array<0,u> { typedef int type; }; 
    template<bool u> struct array<1,u> { typedef float type; }; 
    template<bool u> struct array<2,u> { typedef bool type; }; 

    Instantiate<array2> objects; 
}; 

#include "myLib_workaround_implementation.hpp" 

int main() 
{ 
    test<int> obj; 
    return 0; 
} 
+0

오늘 저녁 VS2010에서이 코드를 테스트 할 것입니다. VS2012 (이 컴파일러에서 컴파일 된 버전) (http://rise4fun.com/Vcpp/1TL)에서 완벽하게 작동합니다. 이 솔루션의 문제점은'test' 후에'Instantiate'의 정의입니다. 내 경우,'test'는 사용자에 의해 제공되고,'Instantiate'는 내 라이브러리에 의해 제공됩니다. 다른 해결 방법/솔루션을 찾으면 게시 해 주시기 바랍니다 :)! – Synxis

+0

@Synxis 내 편집을 참조하십시오 – jerry

+0

그래서 ... CTP! = RTM. Nice>. <... 나는 사용자가 편집에 넣는 해결 방법을 생각했지만 실용적인 것은 아닙니다 (사용자가 구현 헤더를 포함하는 것을 잊지 않도록해야합니다). r4f rtm을 망치는 동안 2 개의 헤더를 포함하지 않는 또 다른 솔루션을 발견했습니다. 답변 감사합니다! – Synxis