次のコードがg ++-4.2でエラーや警告なしにコンパイルされるのを見たとき、私はかなり驚きました:
typedef enum test { one };
私の想定では、typedef
キーワードを使用した場合、次のように追加の識別子が必要になるということでした。
typedef enum test { one } test;
すでに述べたように、g ++-4.2は警告さえ出さずにそれを受け入れます。 Clang ++ 3.0は「警告:typedefには名前が必要です」と警告し、同様にコモーは「警告:宣言にはtypedef名が必要」と警告します "、およびg ++-4.6は通知します:"警告: 'typedef'はこの宣言で無視されました ".
標準でこれが許可されている場所を特定できませんでした。また、2つのコンパイラがrequiredであると警告しているので、少し混乱しています。 typedef-nameがrequiredであるが、存在しない場合、それはエラーではありませんか?
[〜#〜] update [〜#〜]:同じコンパイラでCをチェックインしました。 Clangとcomeauは同じ出力を生成し、gccは警告を出します:「警告:空の宣言での無用のストレージクラス指定子」、これはさらに混乱するようです。
[〜#〜] update [〜#〜]:列挙型の名前の削除を確認しましたが、結果は同じです:
typedef enum { one };
同様に、名前付き構造体を使用:
typedef struct named { int x };
名前のない構造体ではありません。その場合、コードはg ++(4.2/4.6)で拒否され、「error:missing type-name in typedef-declaration "、gcc(4.2/4.6)は警告を出しました:"warning:unnamed struct/union that define no instances "、clang ++"警告:宣言は何も宣言していません "、コモー"エラー:宣言にはtypedef名が必要です 」
これは許可されているが、利点がない縮退構文です。最近のほとんどのコンパイラは、それに関する警告を発するように誘発される可能性があります。デフォルトでは、そうでない場合があります。 typedef名がない場合、キーワードtypedef
は不要です。あなたの例では、それは完全に次と同等です:
enum test { one };
それが発生する可能性のある別の場所は構造です:
typedef struct SomeThing { int whatever; };
これは次と同等です。
struct SomeThing { int whatever; };
typedef
は、公式に(または構文的に)static
、extern
、auto
、register
のような「ストレージクラス指定子」であることに注意してください。
ISO/IEC 9899:1999(これはC規格です)では、次のことがわかります。
§6.7宣言
構文
宣言:
宣言指定子 init-declarator-listopt;
宣言指定子:
storage-class-specifier 宣言指定子opt
タイプ指定子 宣言指定子opt
type-qualifier 宣言指定子opt
関数指定子 宣言指定子opt
init-declarator-list:
init-declarator
init-declarator-list、init-declarator
init-declarator:
宣言子
宣言子=イニシャライザ
そして(要求に応じて):
§6.7.1ストレージクラス指定子
構文
ストレージクラス指定子:
typedef
extern
static
auto
register
その構文をたどると、多くの退化した可能性があり、あなたが示したものはその多くの1つにすぎません。
C++には異なる規則がある可能性があります。
ISO/IEC 14882:1998(元のC++標準)では、§7.1.1 'ストレージクラス指定子'で、C++がtypedef
をストレージクラスとして扱わないことがわかりました。リストはmutable
を追加し、typedef
を除外します。したがって、C++でのtypedef
の文法仕様は、C仕様とは明らかに異なります。
§7宣言
宣言は、名前の解釈方法を指定します。宣言の形式は
宣言シーケンス:
宣言
宣言-順序宣言
宣言:
ブロック宣言
関数定義
テンプレート宣言
明示的なインスタンス化
明示的な専門化
リンケージ仕様
名前空間定義
ブロック宣言:
簡単な宣言
asm定義
名前空間エイリアス定義
使用宣言
usingディレクティブ
簡単な宣言:
decl-specifier-seqopt init-declarator-listopt ;
...
¶5decl-specifier-seqに
typedef
指定子が含まれている場合、宣言はtypedef
宣言と呼ばれ、各init-declarator
は、関連する型(7.1.3)と同義のtypedef-nameとして宣言されています。§7.1指定子[dcl.spec]
宣言で使用できる指定子は次のとおりです。
decl-specifier:
ストレージクラス指定子
タイプ指定子
関数指定子
friend
typedef
decl-specifier-seq:
decl-specifier-seqopt
decl-specifier
§7.1.1ストレージクラス指定子[dcl.stc]
ストレージクラス指定子:
auto
register
static
extern
mutable
§7.1.2関数指定子[dcl.fct.spec]
関数指定子:
inline
virtual
explicit
§7.1.3typedef指定子[dcl.typedef]
Decl-specifier
typedef
を含む宣言は、基本(3.9.1)または複合(3.9.2)型の命名に後で使用できる識別子を宣言します。typedef
指定子は、関数定義(8.4)では使用できません。また、型指定子以外の他の種類の指定子とdecl-specifier-seqで組み合わせてはなりません。typedef-name:
識別子
...
特定のスコープでは、typedef指定子を使用して、そのスコープで宣言されている型の名前を再定義し、すでに参照している型を参照できます。 [例:
typedef struct s { /* ... */ } s; typedef int I; typedef int I; typedef I I;
—例を終了]
§7.1.4フレンド指定子[dcl.friend]
フレンド指定子は、クラスメンバーへのアクセスを指定するために使用されます。 11.4を参照してください。
§7.1.5タイプ指定子[dcl.type]
タイプ指定子:
単純型指定子
クラス指定子
列挙型指定子
エラボレートタイプ指定子
cv-qualifier
§7から¶5は、typedef
の名前はinit-declaratorとinitに由来すると述べています-declarator-listは 'opt'とタグ付けされています。これは、typedef
名がCと同様に、C++では省略できます。
私が見つけることができた唯一のものは、C++ 03標準§7.1.3 [dcl.typedef] p1
:
typedef-name:
- 識別子
typedef
指定子で宣言された名前は、typedef-nameになります。
不足していることに注意してくださいoptidentifierの後、少なくとも私には、identifierがtypedef-name。テストされたすべてのコンパイラがこれを(サイレントに)受け入れることは奇妙です。
編集:@Jonathanの回答の後、私は上記と同じ標準で以下を見つけました:
decl-specifier:
- storage-class-specifier
- タイプ指定子
- 関数指定子
friend
typedef
ご覧のように、これはtypedef
の追加のケースを提供し、storage-class-specifiersのリストはこれを確認します。
ストレージクラス指定子:
auto
register
static
extern
mutable
したがって、C++の場合と同じように無知です。
私には、C対C++の違いのように見えます。 C++は、暗黙的にtypedef構造体と共用体をタグに付けます。そのため、typedefの追加は不必要ですが、エラーではありません。これが列挙型でも機能するかどうかはわかりません。
次に行うことは、これらの宣言の後に許可される変数定義を確認することです。
enum test etest;
test etest2;
struct named snamed;
named snamed2;