web-dev-qa-db-ja.com

typedef宣言ではtypedef-nameはオプションですか?

次のコードが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は、公式に(または構文的に)staticexternautoregisterのような「ストレージクラス指定子」であることに注意してください。


Cスタンダード

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-listinit-declarator

init-declarator

宣言子

宣言子=イニシャライザ

そして(要求に応じて):

§6.7.1ストレージクラス指定子

構文

ストレージクラス指定子:

typedef

extern

static

auto

register

その構文をたどると、多くの退化した可能性があり、あなたが示したものはその多くの1つにすぎません。


C++標準

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-declaratorinitに由来すると述べています-declarator-listは 'opt'とタグ付けされています。これは、typedef名がCと同様に、C++では省略できます。

44

私が見つけることができた唯一のものは、C++ 03標準§7.1.3 [dcl.typedef] p1

typedef-name:

  • 識別子

typedef指定子で宣言された名前は、typedef-nameになります。

不足していることに注意してくださいoptidentifierの後、少なくとも私には、identifiertypedef-name。テストされたすべてのコンパイラがこれを(サイレントに)受け入れることは奇妙です。


編集:@Jonathanの回答の後、私は上記と同じ標準で以下を見つけました:

decl-specifier

  • storage-class-specifier
  • タイプ指定子
  • 関数指定子
  • friend
  • typedef

ご覧のように、これはtypedefの追加のケースを提供し、storage-class-specifiersのリストはこれを確認します。

ストレージクラス指定子:

  • auto
  • register
  • static
  • extern
  • mutable

したがって、C++の場合と同じように無知です。

3
Xeo

私には、C対C++の違いのように見えます。 C++は、暗黙的にtypedef構造体と共用体をタグに付けます。そのため、typedefの追加は不必要ですが、エラーではありません。これが列挙型でも機能するかどうかはわかりません。

次に行うことは、これらの宣言の後に許可される変数定義を確認することです。

enum test etest;
test etest2;
struct named snamed;
named snamed2;
0
luser droog