2

VS2010で奇妙な問題に直面しています。次のコードはコンパイルに失敗します(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>
4

3 に答える 3

1

これはVS2010(with Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86)で再現できますが、これはバグだと思います。正直なところ、ここに含まれるスコープの問題に頭を悩ませることは困難ですが、それは目前の問題とは関係がないようです(ベーステンプレートとスペシャライゼーションが一緒にネストされており、VSが再帰レベルについて不平を言っているため) 、未定義のシンボルではありません)。いずれにせよ、それは非常に興味深い例です。VS 2005(with Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86)でも同じ動作が見られます。MicrosoftがVS2010のバグレポートをまだ受け入れていないようです(Microsoft ConnectVisualStudioページフィードバックフォームから判断して)。問題がまだ修正されていない場合は、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を参照してください。CTPの代わりにデフォルトのオプションRTMが私を超えているのはなぜですか。この例は間違いなく奇妙なものです。

Instantiateによって使用されるライブラリヘッダー以外に何かありますtestか?そうでない場合、ユーザーは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;
}
于 2013-03-26T17:26:10.697 に答える
0

これはバグではなく、コンパイラの構成です。コンパイル時のスタックは、実行時のスタックと同じように制限されます。VSのIIRCは、コンパイル時スタックの最大深度が512です。ドキュメントで調べる価値があるかもしれません。確かではありませんが、構成可能かもしれません。

于 2013-03-25T17:11:20.757 に答える
0

rise4funとjerryの回避策をいじりながら、Instantiate 内部 test の定義全体を「解決」したことがわかりました(VS2012CTPとVS2012RTMの詳細については、jerryの回答を参照してください)。

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;
}

これは、ユーザーがすでに使用しているマクロに含めることができるため、私のユースケースに最適です。

于 2013-03-27T20:37:28.560 に答える