2012-03-12 3 views
8

次のコードはGCC(私はgcc-4.3.4を使用するideoneを使用しました)ではコンパイルしますが、Visual Studioではコンパイルしません。それは標準的なコードとVisual C++ 2008と2010のバグ(私は両方でそれを試しました)または非標準であり、GCCはそれをコンパイルするのがうれしいですか?次のテンプレート特殊化コードは非標準ですか、VS-C++のバグですか?

namespace cool 
{ 
    template <bool, typename = void> struct enable_if {}; 
    template <typename T> struct enable_if<true, T> { typedef T type; }; 

    template <typename T0, typename T1> struct is_same { enum { value = false }; }; 
    template <typename T> struct is_same<T, T> { enum { value = true }; }; 
} 

struct BasePolicy {}; 
struct BasePolicy2 {}; 
struct Faz {}; 

template <typename Policy, 
typename = typename cool::enable_if<cool::is_same<BasePolicy, Policy>::value || cool::is_same<BasePolicy2, Policy>::value>::type > 
struct Foo; 

template <typename Policy> 
struct Foo<Policy> { 
    Foo(); 
}; 

template <typename Policy> 
Foo<Policy>::Foo() { 
} 

int main() 
{ 
    Foo<BasePolicy2> fb; 
    // Foo<Faz> fb1; 
} 

エラー1エラーC2039: '{CTORは}':問題は、アウトオブライン定義であることを 'foo' でmain.cppに25

注のメンバーではありませんFooのコンストラクタです。あなたはクラスで定義する場合は、ビジュアル-C++は幸せです:

namespace cool 
{ 
    template <bool, typename = void> struct enable_if {}; 
    template <typename T> struct enable_if<true, T> { typedef T type; }; 

    template <typename T0, typename T1> struct is_same { enum { value = false }; }; 
    template <typename T> struct is_same<T, T> { enum { value = true }; }; 
} 

struct BasePolicy {}; 
struct BasePolicy2 {}; 
struct Faz {}; 

template <typename Policy, 
    typename = typename cool::enable_if<cool::is_same<BasePolicy, Policy>::value>::type > 
struct Foo; 

template <typename Policy> 
struct Foo<Policy> { 
    Foo(); 
}; 

template <typename Policy> 
Foo<Policy>::Foo() { 
} 

int main() 
{ 
    Foo<BasePolicy> fb; 
    // Foo<Faz> fb1; 
} 

template <typename Policy> 
struct Foo<Policy> { 
    Foo() {} 
}; 

また、次のコードは、両方の(それが欠落している||とロジックの後にあることに注意してください)にコンパイル


クレジットが原因であるクレジットが、これはディートマーKühlによって私に与えられたわずかに修正したバージョンです)

+0

Visual Studioが実際に提供しているエラーは何ですか? –

+0

@James:質問にエラーを追加しました – Samaursa

+0

Dietmarがそのアプローチを提案した元の質問にリンクする必要があります。また、彼の答えにこの質問へのリンクを付けると、彼の注意を引くかもしれない。彼はこの質問に対する答えを知っていると確信しています。 –

答えて

0

これはコンパイラのbugに関連しているようです。

0

ああ。それはすべきではありません:

template <typename Policy> 
struct Foo { 
    Foo(); 
}; 

template <typename Policy> 
Foo<Policy>::Foo() { 
} 

の部分特殊化を宣言する必要があり、標準の言うことを確認してください、しかし、構造体名の後にテンプレートパラメータを持つテンプレート構造体を宣言していない(構造体の宣言で<Policy>なしで。)そのテンプレート。

+0

いいえ、そうしてはいけません。 OPのコードでは、あなたが宣言しているのは、以前に宣言された*テンプレート 'Foo'の*部分的な特殊化*です。構文は部分的な特殊化のために正しいです。あなたが提案しているものは、テンプレートの「主な」宣言には正しいものです。しかし、OPのコードでは、それは主要な宣言ではなく、むしろ専門化です。 – AnT

+0

ああ、愚かな私。私はコード全体を見るために全部スクロールしませんでした。 :) –

1

Visual C++ 2008/2010に誤りがあります。しかし、それは複数の方法で取り組むことができます。

Foo<BasePolicy2> fbのタイプを考えてみましょう。

この宣言では、テンプレートFoo <>の2番目のテンプレートパラメータが最初に宣言されているようにデフォルト設定されています。だから、明示的にそのタイプがある:

/*1*/ Foo<BasePolicy2,cool::enable_if< 
      cool::is_same<BasePolicy, BasePolicy2>::value || 
      cool::is_same<BasePolicy2,BasePolicy2>::value 
     >::type 
    > 

すでに/*1*/はつまるところことに満足している場合:

/*2*/ Foo<BasePolicy2,void> 

、あなたは以下、ランデブーで再び私たちを満たすことができます。

/*3/ cool::enable_if< 
     cool::is_same<BasePolicy, BasePolicy2>::value || 
     cool::is_same<BasePolicy2,BasePolicy2>::value 
    > 

に解決:

/*4/ cool::enable_if<some_boolean_consant> 

次は、template enable_if<>が定義されているかを確認しましょう。

まあ、我々はタイプがあることがわかりますnamespace coolでは、我々は持っている:

/*5/ template <bool, typename = void> struct enable_if {}; 
/*6/ template <typename T> struct enable_if<true, T> { typedef T type; }; 

ので/*4*/ひいてはtemplate enable_if<>の第二テンプレートパラメータをデフォルト設定、およびそのデフォルトのタイプがvoidです。

その最初のブールパラメータは 値trueを有しており、その場合に、enable_if<>が第2テンプレート のタイプを有するのtypedef typeをエクスポートしなければならないと言うたびに[OK]を、その後/*6*/がその第2のテンプレートパラメータに対してtemplate enable_if<>を専門パラメータ。最初のboolパラメータがfalseの場合、そのtypedefは存在せず、コンパイラはbarfを返します。

さてさて、私たちはsome_boolean_consant == trueそして、/*4*/がすべてでコンパイルするかどうかということを知っているし、エクスポートされたタイプtypeは のそれはenable_if<>の不履行第二テンプレートパラメータです。 voidです。それはvoidある

/*7*/ cool::enable_if< 
      cool::is_same<BasePolicy, BasePolicy2>::value || 
      cool::is_same<BasePolicy2,BasePolicy2>::value 
     >::type 

Now'veは、の種類を推定しました。したがって、/*1*//*2*/になります。デフォルトのタイプはFoo<BasePolicy2>です。

それがあるべきようですが、この結論の疑わしいされている場合は、単にグローバルスコープでプログラムにこれを追加してコンパイルします。

typedef cool::enable_if< 
      cool::is_same<BasePolicy, BasePolicy2>::value || 
      cool::is_same<BasePolicy2,BasePolicy2>::value 
     >::type WhatType; 
WhatType what_am_i; 

コンパイラは言うだろう:

'what_am_i' : illegal use of type 'void' 

またはその旨の単語。

ランデブー

= /*2//*1/は私たちに質問を約あることのVisual C++のコンパイルエラーにいくつかの影響力を与えることを知識:

Error 1 error C2039: '{ctor}' : is not a member of 'Foo' main.cpp 25 

エラーがコンストラクタは、それを生じさせることに不平を言っています実際には属している型で宣言されたコンストラクタではありません。つまり、 Foo<Policy>::Foo()Foo<Policy>のコンストラクタではありません。

Foo<Policy>の定義では、初期宣言の2番目のテンプレートパラメータがデフォルトで設定されています。これは、voidであることがわかります。したがって、 質問自体が表示されます:実際には、デフォルトの2番目のテンプレートパラメータをコンパイラが尊重していますか?つまり、 のテンプレート定義Foo<Policy>がテンプレート定義のFoo<Policy,void>であることを認識していますか?

template <typename Policy> 
struct Foo<Policy,void> { 
    Foo(); 
}; 

template <typename Policy> 
Foo<Policy,void>::Foo() { 
} 

は、プログラムはVisual C++でコンパイルクリーン:

答えは、我々は単に明示的にデフォルトの第二パラメータを指定するには、テンプレートとそのコンストラクタの定義を変更した場合についてはNoです。

Visual C++は、このエラーを起こして、Standard C++について議論の余地がありますか?それともちょうど壊れていますか? これはちょうど壊れています。

/* Simple Case */ 

template<typename X, typename Y = void> 
struct A; 

template<typename X> 
struct A<X> { 
    A(); 
}; 

template<typename X> 
A<X>::A(){}; 

int main() 
{ 
    A<int> aint; 
    return 0; 
} 

これは、それが消化不良を引き起こしているテンプレートメタプログラミング/*3*/の一口だことを示して と質問者が指摘するように、場合:私たちは、この単純にそれをしようが、基本的に同じプログラムならば、それは問題ありませんので、その口に合ったものは、||の操作を削除することで実験的に簡略化され、 がすべて正常です(もちろん、cool::論理は壊れています)。

回避策はありますか?

私は既に見ました。 voidのテンプレートパラメータをFoo<Policy>Foo<Folicy>::Foo()のように明示的に定義するだけです。あなたが知りたいことがあれば、今すぐに出ることができます。

しかし、それは痒みです。このレベルでバグが ではないことが判明した場合、修正レベルを/* Simple Case */に適用しています。これは、コンパイラのテンプレートメタプログラミング作業が不明瞭であるため、かゆみがない 回避策は少なくともnamespace coolに限定されます。 がblightersが飛行でインスタンス化してはいけません。ここで

は、私はすぐに私は忘れるようになる コンパイラの進展を期待したテンプレートメタプログラミング(TMP)ヘルパーテンプレートに関する注意のルールです。そのようなテンプレートは、コンパイル時のロジックを容易にするためにのみ 存在します。コンパイラが 型を再帰的に定義するだけでコンパイラがそのロジックを実行できるのであれば、少なくともインスタンス化の深さが保たれている限り、そのコンフォートゾーンにとどまる可能性があります。 コンパイル時のロジックを補完するために中間型をインスタンス化することが義務付けられている場合、その最も奇妙な ペッカジロが現れやすい。あなたはコンパイルロジックが値のみ、彼らが公開する静的または列挙定数の を取ることによって達成することができるようにあなたのTMPのヘルパークラスをコーディングした場合

は、その後、あなただけのためのすべての時間、 をインスタンス化するコンパイラを強要します静的または列挙された定数は値を取得します。

ケースでは、クラス/*3*/は、その不思議な毒性のある||操作です。 namespace coolのTMPヘルパークラスは、不必要にブール定数のメタ論理を で行います。

TMPヘルパークラスの慎重なアプローチは、反復型定義によって、論理演算をインスタンス化しないですべてシミュレーションできるように定義することです。定数がエクスポートされる - 最終的に必要になるのは、コンパイル時 ロジックが正常に上陸しました。

namespace cool 
{ 
    template<bool val> 
    struct truth_type 
    { 
     static const bool value = false; 
    }; 

    template<> 
    struct truth_type<true> 
    { 
     static const bool value = true; 
    }; 

    typedef truth_type<true> true_type; 
    typedef truth_type<false> false_type; 

    template<class lhs,class rhs> 
    struct or_type 
    { 
     typedef false_type type; 
    }; 

    template<class lhs> 
    struct or_type<lhs,true_type> 
    { 
     typedef true_type type; 
    }; 

    template<class rhs> 
    struct or_type<true_type,rhs> 
    { 
     typedef true_type type; 
    }; 

    template <typename T, typename = void> struct enable_if {}; 
    template <typename T> struct enable_if<true_type, T> { typedef T type; }; 

    template <typename T0, typename T1> struct is_same { 
     typedef false_type type; 
    }; 
    template <typename T> struct is_same<T, T> { 
     typedef true_type type; 
    }; 
} 

とテンプレートのFoo <の対応する宣言>次のようになります:namespace coolの内容の慎重な再書き込みは次のようなものが見えるかもしれません

template <typename Policy, 
typename = typename cool::enable_if< 
    typename cool::or_type< 
     typename cool::is_same<BasePolicy, Policy>::type, 
     typename cool::is_same<BasePolicy2, Policy>::type 
    >::type 
>::type> 
struct Foo; 

この方法で、私たちのどれもcool::シバン全体が でインスタンス化されるまで物事がインスタンス化されます:すべてのメタロジックの型だけを扱います。これらの変更がプログラムに行われた場合は、かゆみの回避策は必要ありません。 そしてGCCもそれに満足しています。

関連する問題