10

この質問は、この他の質問に触発されました。その質問に答えようとしているうちに、私は自分自身にたくさんの質問があることを理解しました。だから...次のことを考慮してください:

struct S1
{
    enum { value = 42 };
};

template <class T> struct S2
{
    typedef S1 Type;
};

template <class T> struct S3
{
    typedef S2<T> Type; 
};

template <class T> struct S4
{
    typedef typename T::Type::Type Type;  //(1)//legal?
    enum {value = T::Type::Type::value }; //(2)//legal?
};

int main()
{
    S4<S3<S2<S2<S1> > > >::value;
}

これは、MSVC9.0およびOnlineComeauで正常にコンパイルされます。しかし、気になるのはtypename、(1)で何を言っているのか、(2)でなぜ必要ないのかわからないことですtypename

私はこれらの2つの構文(構文?)を試しましたが、どちらもMSVCでは失敗するはずです。

    typedef typename T::typename Type::Type Type;
    enum {value = typename T::typename Type::Type::value }; 

    typedef typename (typename T::Type)::Type Type;
    enum {value = (typename (typename T::Type)::Type)::value }; 

もちろん、回避策はtypedef次のように連続してを使用することです。

   typedef typename T::Type T1;
   typedef typename T1::Type Type;
   enum { value = Type::value};  

良いスタイルは脇に置いておきますが、構文的には、前述の回避策を使用する必要がありますか

残りは興味深い例です。読む必要はありません。質問にはそれほど関係ありません。

typenameMSVCは複数のs(つまり(1)と(2))のない元の奇妙な構文を受け入れますが、前述の質問のように奇妙な動作を引き起こすことに注意してください。ここでもその例を簡潔な形で提示すると思います。

struct Good
{
    enum {value = 1}; 
};
struct Bad
{
    enum {value = -1};  
};

template <class T1, class T2>
struct ArraySize
{
    typedef Bad Type;
};
template <class T>
struct ArraySize<T, T>
{
    typedef Good Type;
};

template <class T>
struct Boom
{
    char arr[ArraySize<T, Good>::Type::value]; //error, negative subscript, even without any instantiation
};

int main()
{
    Boom<Good> b; //with or without this line, compilation fails.
}

これはコンパイルされません。私が言及した回避策は問題を解決しますが、ここでの問題は私の最初の質問であると確信しています-タイプ名がありませんが、どこに貼り付けるかは本当にわかりません。よろしくお願いします。

4

5 に答える 5

8

スコープ演算子の前の名前は、::常に名前空間またはクラス (または列挙) 名である必要があり、名前空間名は依存できません。したがって、これがクラス名であることをコンパイラに伝える必要はありません。


私はこれを作り上げているだけではありません、標準は言います(セクション[temp.res]):

mem-initializer-id、base-specifier、またはexpected-type-specifierで名前として使用される修飾名は、typenameキーワードを使用しなくても、暗黙的に型に名前を付けると想定されます。テンプレート パラメーターに依存するnested -name-specifierをすぐに含むnested-name-specifierではtypename、キーワードを使用せずに、識別子またはsimple-template-idが暗黙的に型の名前であると見なされます。[ 注:typenameキーワードは、これらの構文の構文では許可されていません。— エンドノート]

T::T::Type::、およびT::Type::Type::ネストされた名前指定子であり、 でマークする必要はありませんtypename

このセクションでは、例外のリストに typedef 宣言の型指定子を含めることができましたし、間違いなく含めるべきでした。ただし、特に typedef 宣言では、型指定子が非常に複雑になる可能性があることに注意してください。現在、 typedef type-specifiertypenameでキーワードが複数回必要になる可能性が非常に高いため、typedef では決して必要ではないことを私に納得させるには、さらに多くの分析が必要になります。typename

ではtypedef typename T::Type::Type Typeネストされた名前指定子( ) が従属名であり、標準で (同じセクション) と述べられているため、キーワードをT::Type::Type使用する必要があります。typenameT::Type::

修飾 ID が現在のインスタンス化 (14.6.2.1) のメンバーではない型を参照することを意図しており、そのネストされた名前指定子が依存型を参照する場合、キーワード typename を前に付けて、タイプ名指定子。typename-specifier の修飾 ID が型を示さない場合、プログラムは不正な形式です。

于 2011-07-10T22:08:05.363 に答える
3

typename のポイントは、インスタンス化する前にテンプレート定義の基本的なチェックを可能にすることです。名前が型であるかどうか (a*b;式ステートメントまたは pointee の宣言であるかどうか) を知らずに C++ を解析することは不可能bです。

テンプレート定義では、単純な識別子のカテゴリ (タイプまたは非タイプ) は常に既知です。ただし、修飾された (従属) 名は指定できません。任意の T の場合、T::x はどちらでもかまいません。

そのため、この言語では、typename キーワードを使用して、修飾名が型を表すことをコンパイラに伝えることができます。そうしないと、コンパイラは値型であると想定する必要があります。どちらの場合でも、コンパイラを誤解させるのはエラーです。

この規則は、型が必要であることが明らかな場合 (typedef など) にも適用されます。

完全修飾名だけがこの明確化を必要とします - typename A::B::CC が型であることを示します。修飾名が表示されるコンテキストを解析するために、A または B について何も知る必要はありません。

あなたの例(1)では、タイプ名はそれがタイプであると言っていT::Type::Typeます。(2) は型ではないため、typename を使用してはなりませんT::Type::valueT::Typeこれは無関係であるため、どちらのケースも何も述べていません。(ただし、型である必要があると推測することはできますが、それ以外の場合は適用できません::。)

MSVC に関するあなたの問題は、単にそのコンパイラのバグだと思います (2 フェーズ ルックアップを適切に処理しないことは有名です) が、100% 確実ではないことは認めざるを得ません。

于 2011-07-10T21:53:08.273 に答える
2
typedef typename T::Type::Type Type;  //(1)//legal?
enum {value = T::Type::Type::value }; //(2)//legal?

(1)では、 T::Type:: Typeは型名であると言います

(2) では T::Type::Type::値について何も言わず、デフォルトでは非型として解析されます

于 2011-07-10T18:36:41.980 に答える
-1

typedef typename T::Type::Type タイプ; //(1)//合法?

私自身、ここの必要性を理解していませんtypename。にのみ適用typedefできるためtypenameです。おそらく、C++ 文法はこのように設計されています。

enum {値 = T::タイプ::タイプ::値}; //(2)//合法?

値であることが期待されるため、使用できません。を書くときは、常に値のみでなければならないというtypenameことは、暗黙のうちに論理的です。enum { value = ??? };???

于 2011-07-10T17:52:03.387 に答える
-4

typename、最初の従属型を参照します。あなたの特定のケースでは:

typedef typename T::type1::type2 Type;

を参照しT::type1、従属名であることを伝えます (テンプレート パラメーター T に依存します)。

定数値の場合、型ではなく値であるため、型名は必要ありません。値が定義されていない場合、コンパイル エラーが発生します。

編集

struct S1
{
    enum { value = 42 };
};
template <class T> struct S2
{
    typedef S1 Type;
};
template <class T> struct S3
{
    typedef S2<T> Type; 
};
template <class T> struct S4
{
    typedef typename T::Type::Type Type;  //(1)//legal?
    enum {value = T::Type::Type::value }; //(2)//legal?
};

例をゆっくり見ていきましょう。この型で起こることは次のS4<S3<S2<S2<S1> > > >とおりです: T はS3<S2<S2<S1> > >であるため、完全な型である にtypename T::Type展開さS2<S2<S1> >::Typeれます (テンプレート パラメーターにはまったく依存しません)。そのため、最初の依存型名の後に typename を使用する必要はありません。

于 2011-07-10T18:02:52.390 に答える