6

テンプレート クラスでローカル クラスを使用して、"static if" などの構造を実行するのが好きです。しかし、gcc 4.8 が私のコードをコンパイルしたくないという問題に直面しました。ただし、4.7 は行います。

このサンプル:

#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

仕事:

  1. 印刷用の異なるインターフェイスを持つクラス A と B を指定します。
  2. A と B の両方を出力できるジェネリック クラス Test を作成します。
  3. 名前空間またはクラス スコープを汚染しないでください。

コードの説明:

  1. これは単なるクリーンな例です。
  2. 「static if」の構造を一般化したいので、このようなアプローチを使用します。引数を直接 print() 関数に渡すのではなく、フィールドを介して IfA および IfB クラスに渡していることを確認してください。
  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() はありません。(後述: 'void Test::print()::IfB::print() [with 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();
}

エラー:

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();
                 ^

2012.10 と 2013.02 の 2 つの GCC 4.8 バージョンをテストしました。それが GCC 4.8 のバグであり、修正できることを願っています。

4

2 に答える 2

3

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

SFINAE は、テンプレートの引数推定および引数置換中に適用されます (SFINAE の 'S' は置換を表します) が、プログラムで唯一の置換は、 のテンプレート パラメーター リストで を置換Aするときに発生します。これは失敗しません。TTest

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

SFINAE は、関数呼び出しによって引き起こされたテンプレート引数推定、またはデフォルト引数でテンプレート パラメーターを推定する場合など、置換コンテキストで使用する必要があります。

于 2013-03-17T16:56:47.560 に答える
3

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

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

于 2013-03-17T04:28:04.480 に答える