2013-03-17 9 views
6

"static if"のような構造を実行するために、テンプレートクラスでローカルクラスを使用するのが好きです。しかし、gcc 4.8は自分のコードをコンパイルしたくないという問題に直面しました。しかし4.7はそうです。C++ GCCこのsfinaeコードはGCC 4.7でコンパイルできますが、4.8ではコンパイルできないのはなぜですか?

このサンプル:

#include <type_traits> 
#include <iostream> 
#include <string> 

using namespace std; 

struct A { 
    void printA() { 
     cout << "I am A" << endl; 
    } 
}; 
struct B { 
    void printB() { 
     cout << "I am B" << endl; 
    } 
}; 

template <typename T> 
struct Test { 
    void print() { 
     struct IfA { 
      constexpr IfA(T &value) : value(value) { 
      } 
      T &value; 
      void print() { 
       value.printA(); 
      } 
     }; 
     struct IfB { 
      constexpr IfB(T &value) : value(value) { 
      } 
      T &value; 
      void print() { 
       value.printB(); 
      } 
     }; 
     struct Else { 
      constexpr Else(...) {} 
      void print() { 
      } 
     }; 
     typename conditional<is_same<T, A>::value, IfA, Else>::type(value).print(); 
     typename conditional<is_same<T, B>::value, IfB, Else>::type(value).print(); 
    } 
    T value; 
}; 

int main() { 
    Test<A>().print(); 
    Test<B>().print(); 
} 

オプション:

g++ --std=c++11 main.cc -o local-sfinae 

タスク:印刷用の別のインタフェースとクラスA及びBを考える

  1. AとBの両方を印刷できる汎用クラスのテストを記述してください。
  2. 名前空間またはクラススコープのいずれも汚染しないでください。

コードの説明:

  1. これが唯一のクリーンな例です。
  2. 私はこのようなアプローチを使用します。なぜなら私は構造を "静的if"に一般化したいからです。参照してください。引数をifAクラスとIfBクラスに渡すのは、print()関数に直接渡すのではなく、フィールドを使って行います。
  3. 私はそのような構成をたくさん使っています。
  4. 私は、これらの構成が(汚染)クラススコープにあってはならないことを発見しました。つまり、メソッドのスコープに配置する必要があります。

このコードはGCC 4.8でコンパイルできません。使用されていなくても、すべてのクラスをチェックするためです。しかし、バイナリでインスタンス化していません(私はエラーの原因となる行をコメントし、gcc 4.8でコンパイルしました)。証明:

$ nm local-sfinae |c++filt |grep "::If.*print" 
0000000000400724 W Test<A>::print()::IfA::print() 
00000000004007fe W Test<B>::print()::IfB::print() 

参照:Test :: print():: IfB :: print()はありません。 (後述: ')(ボイドテスト::印刷:: IFB ::プリントを()とT = A]')

エラーを私はGCC 4.8で前述のコードをコンパイルする場合:

g++ --std=c++11 main.cc -o local-sfinae 
main.cc: In instantiation of 'void Test<T>::print()::IfB::print() [with T = A]': 
main.cc:36:9: required from 'void Test<T>::print() [with T = A]' 
main.cc:49:21: required from here 
main.cc:34:17: error: 'struct A' has no member named 'printB' 
       value.printB(); 
       ^
main.cc: In instantiation of 'void Test<T>::print()::IfA::print() [with T = B]': 
main.cc:28:9: required from 'void Test<T>::print() [with T = B]' 
main.cc:50:21: required from here 
main.cc:26:17: error: 'struct B' has no member named 'printA' 
       value.printA(); 
       ^
  1. GCC 4.8バグですか?
  2. GCC 4.7バグですか?コードをコンパイルしないでください。
  3. これは私のバグです。コンパイラの動作に頼るべきではありません。「静的if」を実装するためにこのようなアプローチを使用すべきではありません。

追加情報:

この単純なコードは4.7でコンパイルではなく、上の4.8。私はそれを短縮した。

struct A { 
    void exist() { 
    } 
}; 

template <typename T> 
struct Test { 
    void print() { 
     struct LocalClass { 
      constexpr LocalClass(T &value) : value(value) { 
      } 
      T &value; 
      void print() { 
       value.notExist(); 
      } 
     }; 
    } 
    T value; 
}; 

int main() { 
    Test<A>().print(); 
} 

エラー:2012.10と2013.02:

main.cc: In instantiation of 'void Test<T>::print()::LocalClass::print() [with T = A]': 
main.cc:16:9: required from 'void Test<T>::print() [with T = A]' 
main.cc:22:21: required from here 
main.cc:14:17: error: 'struct A' has no member named 'notExist' 
       value.notExist(); 
       ^

は2つのGCC 4.8のバージョンをテストしました。 GCC 4.8バグだと思いますが、修正することができます。

+0

"どの診断基準も"無効であることを示す標準についての不正な形式のコード。コンパイラはコンパイルを拒否することがあります。 –

答えて

3

LocalClassはテンプレートではありません。 「使用されていない場合はインスタンス化されない」ルールは、クラステンプレートのメンバー関数にのみ適用されます。

つまり、Test::print()がインスタンス化されると、そのローカルクラスの未使用メンバーを含め、内部のすべてが有効になります。

3

コードにSFINAEはありません。

SFINAEは、テンプレート引数控除と引数の置換の際に適用されます(SFINAEで「S」の置換を意味する)が、失敗しないTestのテンプレートパラメータリストにTためAを代入するとき、あなたのプログラムの唯一の置換が起こります。

次に、置換を含まないTest<A>::print()をインスタンス化するprint()を呼び出します。value.notExist();が無効であるため、エラーが発生します。

SFINAEは、関数呼び出しによるテンプレート引数の減算や、デフォルト引数でテンプレートパラメータを推測するときなど、置換コンテキストで使用する必要があります。

+0

私は、に多くのsfinaeコードが含まれていることを意味しています。しかし、GCC 4.7も4.8も実際にLocalClassをインスタンス化していないようです。しかし、GCC 4.8もエラーを返します。ご意見ありがとうございます。私はより小さなタイプのスイッチング機構を作りました:http://ideone.com/v1KmTA – cppist

+0

@ n.m。他の答えでは、 'LocalClass'はテンプレートではないので、インスタンス化することはできません。定義が無効です。周囲の関数テンプレートがインスタンス化されたときに診断されます。 –

関連する問題