web-dev-qa-db-ja.com

typedefsと#defines

typedefsと#definesいずれか。今日は彼らと仕事をしながら、物事を考え始めました。

別の名前でintデータ型を使用するには、以下の2つの状況を考慮してください。

typedef int MYINTEGER

そして

#define MYINTEGER int

上記の状況と同様に、多くの状況で、#defineを使用して非常にうまく実行でき、typedefを使用して同じことを実行できますが、同じ方法はかなり異なる場合があります。 #defineは、typedefではできないMACROアクションも実行できます。

それらを使用する基本的な理由は異なりますが、それらの動作はどのように異なりますか?両方を使用できる場合、どちらを優先するかまた、どの状況で1つが他よりも高速であることが保証されていますか? (たとえば、#defineはプリプロセッサディレクティブであるため、すべてコンパイルまたは実行時よりも早く実行されます)。

32
c0da

マクロが特に必要であるという奇妙な理由がない限り、typedefが一般的に推奨されます。

マクロはテキストによる置換を行うため、コードのセマンティクスにかなりの暴力を振るう可能性があります。たとえば、次の場合:

#define MYINTEGER int

あなたは合法的に書くことができます:

short MYINTEGER x = 42;

short MYINTEGERshort intに展開されるためです。

一方、typedefの場合:

typedef int MYINTEGER:

名前MYINTEGERtypeintの別の名前であり、キーワード「int」のテキストによる置換ではありません。

型が複雑になると、事態はさらに悪化します。たとえば、次の場合:

typedef char *char_ptr;
char_ptr a, b;
#define CHAR_PTR char*
CHAR_PTR c, d;

ab、およびcはすべてポインターですが、dcharです。これは、最後の行が次のように展開されるためです。

char* c, d;

これは

char *c;
char d;

(ポインター型のTypedefは通常良い考えではありませんが、これは要点を示しています。)

別の奇妙なケース:

#define DWORD long
DWORD double x;     /* Huh? */
67
Keith Thompson

はるかに、マクロの最大の問題は、それらがスコープされないことです。それだけでtypedefの使用が保証されます。また、意味的にはより明確です。コードを読んだ人が定義を見たとき、彼はそれを読んでマクロ全体を理解するまで、それが何であるかを知りません。 typedefは、タイプ名が定義されることをリーダーに通知します。 (私はC++について話していることを述べておかなければなりません。Cのtypedefスコープについてはわかりませんが、それは似ていると思います)。

15
Tamás Szelei

ソースコードは主に仲間の開発者向けに書かれています。コンピューターは、コンパイルされたバージョンを使用します。

この観点では、typedef#defineはしません。

15
mouviciel

キース・トンプソンの回答 は非常に優れており、 タマス・ゼレイの追加ポイント とともにスコーピングは、必要なすべての背景を提供する必要があります。

常にマクロを絶望的な最後の手段と考える必要があります。マクロでしかできないことがいくつかあります。それでも、本当にやりたいのなら、じっくり考えなければなりません。微妙に壊れたマクロによって引き起こされる可能性のあるデバッグの苦痛はかなりのものであり、前処理されたファイルを調べる必要があります。 C++での問題の範囲を把握するには、一度だけ実行する価値があります。前処理されたファイルのサイズだけでも目を見張る可能性があります。

6

スコープとテキストの置換に関するすべての有効な説明に加えて、#defineもtypedefと完全に互換性がありません!つまり、関数ポインタの場合です。

_typedef void(*fptr_t)(void);
_

これは、型void func(void)の関数へのポインタである型_fptr_t_を宣言します。

この型をマクロで宣言することはできません。 #define fptr_t void(*)(void)は明らかに機能しません。 #define fptr_t(name) void(*name)(void)のようなあいまいなものを書く必要がありますが、これはコンストラクターがないC言語では実際には意味がありません。

配列ポインタは#defineで宣言できません:typedef int(*arr_ptr)[10];


また、Cは言及する価値のある型の安全性について言語サポートを備えていませんが、typedefと#defineに互換性がないもう1つのケースは、疑わしい型変換を行う場合です。 typedefを使用すると、コンパイラーやastaticアナライザーツールがそのような変換に対して警告を表示できる場合があります。

5
user29079

仕事を成し遂げる力が最も少ないツールと、警告が最も多いツールを使用します。 #defineはプリプロセッサで評価されますが、大部分は自分で行います。 typedefはコンパイラーによって評価されます。名前が示すように、チェックが与えられ、typedefはタイプのみを定義できます。したがって、あなたの例では間違いなくtypedefに行きます。

4
Martin

定義に対する通常の引数を超えて、マクロを使用してこの関数をどのように記述しますか?

template <typename IterType>
typename IterType::value_type Sum(
    const IterType& begin, 
    const IterType& end, 
    const IterType::value_type& initialValue)
{
    typename IterType::value_type result = initialValue;
    for (IterType i = begin; i != end; ++i)
        result += i;

    return result;
}

....

vector<int> values;
int sum = Sum(values.begin(), values.end(), 0);

これは明らかに自明な例ですが、その関数は加算*を実装するタイプの前方反復可能なシーケンスを合計できます。このように使用されるTypedefは Generic Programming の重要な要素です。

*私はこれをここに書いたところですが、読者のための演習としてコンパイルしておきます:-)

編集:

この答えは多くの混乱を引き起こしているようですので、それをもっと引き出します。 STLベクトルの定義の内部を見ると、次のようなものが表示されます。

template <typename ValueType, typename AllocatorType>
class vector
{
public:
    typedef ValueType value_type;
...
}

標準コンテナー内でtypedefを使用すると、(上記で作成したような)汎用関数がこれらの型を参照できます。関数「Sum」は、コンテナのタイプ(std::vector<int>)、コンテナ内に保持されているタイプ(int)ではありません。 typedefがないと、その内部型を参照することはできません。

したがって、typedefはModern C++の中心であり、これはマクロでは不可能です。

2
Chris Pitman

デバッガでこれが意味することを忘れないでください。一部のデバッガーは#defineをうまく処理しません。使用するデバッガーで両方をいじってみてください。書くよりも読むことに多くの時間を費やすことを覚えておいてください。

1
anon

typedefはC++の理念に適合します。コンパイル時に可能なすべてのチェック/アサートです。 #defineは、多くのセマンティクスをコンパイラに隠すプリプロセッサトリックです。コードの正確さ以上にコンパイルのパフォーマンスを気にする必要はありません。

新しいタイプを作成すると、プログラムのドメインが操作する新しい「もの」を定義することになります。したがって、この「もの」を使用して関数とクラスを作成し、コンパイラーを使用して静的チェックを行うことができます。とにかく、C++はCと互換性があるため、警告を生成しないintとintベースの型の間には多くの暗黙的な変換があります。したがって、この場合、静的チェックの能力を十分に活用できません。ただし、typedefenumに置き換えて、暗黙的な変換を見つけることができます。例:typedef int Age;、次にenum Age { };Ageintの間の暗黙的な変換により、あらゆる種類のエラーが発生します。

もう1つ:typedefnamespaceの中に置くことができます。

1
dacap

Typedefの代わりにdefineを使用することは、別の重要な側面、つまり型の特性の概念の下で厄介です。すべての特定のtypedefを定義するさまざまなクラス(標準コンテナーと考え​​てください)を検討してください。 typedefを参照することにより、汎用コードを記述できます。この例には、次のような一般的なコンテナー要件(c ++標準23.2.1 [container.requirements.general])が含まれます。

X::value_type
X::reference
X::difference_type
X::size_type

これはスコープされていないため、マクロでは一般的に表現できません。

1
Francesco