2013-03-25 97 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...) 

製作test一個非模板(即,中庸之道除去線template<typename O>)解決了錯誤。然而,儘管在這個簡單的例子中是可能的,但在我的代碼中這是不可能的(這更復雜)。

的問題是:

  • 哪個是正確的,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 – 2013-03-25 17:08:07

+0

@Shafik我已經看到它,它不是那個錯誤。不管怎樣,謝謝你。 – Synxis 2013-03-25 17:09:44

+0

您是否嘗試過將array2模板移動到數組<2,u>專業化?這有什麼不同嗎? – Tomek 2013-03-25 18:40:49

回答

0

雖然與rise4fun和傑裏的解決方法搞亂,我發現推杆Instantiate整個定義中test'solved' the problem看到傑裏的回答對VS2012CTP VS VS2012RTM更多信息):

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 2013-03-27 20:59:41

+0

是的。它將成爲[SPARK Particle Engine](http://spark.developpez.com/index.php?lang=en)的一部分(主頁被竊聽,您可能會在論壇和源代碼中找到更多信息) 。 – Synxis 2013-03-28 00:25:30

0

這是不是一個錯誤,它的編譯器配置。編譯時堆棧與運行時堆棧一樣有限。 IIRC中的編譯時堆棧的最大深度爲512.在文檔中可能值得尋求,也許它是可配置的,但不確定。

+0

對於gcc也是一樣。而且,類型數組只有3個元素。所以它應該停止'n == 3'的遞歸,這與'void'專精相對應。 GCC做到了。 – Synxis 2013-03-25 17:13:07

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)。似乎微軟仍然接受VS 2010的錯誤報告(由Microsoft Connect Visual Studio pagefeedback form來判斷)。希望有人能夠訪問VS 2012,將能夠測試您的原始代碼,並提交錯誤報告,如果問題尚未解決(我認爲可能,我沒有看到任何跡象表明它已經引起了微軟的注意)。

要解決該問題,向前聲明這是造成遞歸類模板:

// 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。爲什麼CTP是默認選項,而不是RTM超出了我。這個例子絕對是一個奇怪的例子。

test使用的磁帶庫標題中是否有除Instantiate之外的其他內容?如果不是,用戶可以明確地轉發聲明Instantiate模板定義test,然後包含您的標題。否則,您可以創建一個頭文件,其中包含前向聲明以及所需的其他內容。很有可能您希望爲受此問題影響的用戶創建特殊的解決方法標題。這無疑使事情變得非常混亂,而且容易出錯(主要是由於默認模板參數),但它會工作:

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'由我的庫... +1。如果您發現另一個解決方法/解決方案,請隨時發佈:)! – Synxis 2013-03-27 17:04:24

+0

@Synxis請看我的編輯 – jerry 2013-03-27 19:28:58

+0

所以...... CTP!= RTM。 Nice>。<...我想到了你在編輯中放置的解決方法,不太實際(你必須確保用戶不會忘記包含實現頭文件)。在搞亂r4f rtm的同時,我發現了另一個不包含2個頭文件的解決方案。謝謝你的回答! – Synxis 2013-03-27 20:34:03