C ++ 11の「typedef」と「using」の違いは何ですか?


905

C ++ 11ではusingtypedefsのような型エイリアスを書き込むために使用できることを知っています。

typedef int MyInt;

私が理解していることから、これは次と同等です:

using MyInt = int;

そして、その新しい構文は、 " template typedef" を表現する方法を持つための努力から生まれました。

template< class T > using MyType = AnotherType< T, MyAllocatorType >;

しかし、最初の2つの非テンプレートの例では、標準に他の微妙な違いがありますか?たとえば、typedefsは「弱い」方法でエイリアシングを行います。つまり、新しいタイプは作成されず、新しい名前のみが作成されます(これらの名前間の変換は暗黙的です)。

それと同じusingですか、それとも新しいタイプを生成しますか?違いはありますか?


215
新しい構文は通常の変数の割り当てと非常によく似ているため、個人的には読みやすくなっています。たとえば、あなたはどちらを好みますtypedef void (&MyFunc)(int,int);using MyFunc = void(int,int);
Matthieu M.

13
私は完全に同意します。今は新しい構文のみを使用します。だから、私は本当に違いがないことを確認するように求めていました。
Klaim

80
@MatthieuM。これら2つは異なるです。それはする必要がありますtypedef void MyFunc(int,int);(実際には悪いようには見えないもの)、またはusing MyFunc = void(&)(int,int);
R.マルティーニ・フェルナンデス

5
あなたが必要なのですか、なぜR.MartinhoFernandes @ (&)using MyFunc = void(&)(int,int);MyFunc関数への参照であることを意味しますか?を省略した場合はどうなりますか?
リッチ

7
はい、それは関数リファレンスです。と同等typedef void (&MyFunc)(int,int);です。省略した場合は、次&と同等ですtypedef void MyFunc(int,int);
R.マルティーニョフェルナンデス

回答:


585

それらは標準(強調鉱山)(7.1.3.2)から同等です:

typedef-nameは、エイリアス宣言によって導入することもできます。usingキーワードに続く識別子はtypedef-nameになり、識別子に続くオプションのattribute-specifier-seqはそのtypedef-nameに付随します。typedef指定子によって導入された場合と同じセマンティクスを持っています。特に、新しいタイプを定義せず、type-idに表示されません。


24
答えから、usingキーワードはのスーパーセットのようtypedefです。その後、typdef将来的に廃止されますか?
iammilind

49
非推奨は必ずしも削除する意図を示しているわけではありません。これは、別の手段を優先することを強く強く推奨しているにすぎません。
Iron Savior

18
しかし、なぜそれらがtypedefのテンプレート化だけを許可しなかったのかと思います。セマンティクスがテンプレートでうまく機能しなかったusingために構文が導入されたことをどこかで読んだことを覚えていますtypedef。これusingは、まったく同じセマンティクスを持つと定義されているという事実に何らかの形で矛盾します。
celtschk 2013

12
@celtschk:その理由は提案書で説明されていn1489ます。テンプレートエイリアスはタイプのエイリアスではなく、テンプレートのグループのエイリアスです。typedefフェルトを区別するために、新しい構文の必要性。また、OPの問題は非テンプレートバージョン間の違いについてであることに注意してください。
ジェシーグッド

4
では、なぜこの冗長性が導入されたのでしょうか?同じ目的のための2つの構文。そして、私はtypdefこれまで廃止されたとは思いません。
ブノワ

237

次の点を除いて、ほとんど同じです。

エイリアス宣言はテンプレートと互換性がありますが、Cスタイルのtypedefは互換性がありません。


29
特に答えの単純さが好きで、タイプセットの起源を指摘しています。
g24l 2015年

1
@ g24lあなたはtypedefを意味します... おそらく
Marc.2377

CとC ++の違いは何typedefですか?
McSinyx

196

使用して、テンプレート内で使用する場合の構文は利点があります。タイプの抽象化が必要であるが、将来的に指定できるようにテンプレートパラメータを保持する必要がある場合。このようなものを書くべきです。

template <typename T> struct whatever {};

template <typename T> struct rebind
{
  typedef whatever<T> type; // to make it possible to substitue the whatever in future.
};

rebind<int>::type variable;

template <typename U> struct bar { typename rebind<U>::type _var_member; }

ただし構文を使用すると、この使用例が簡素化されます。

template <typename T> using my_type = whatever<T>;

my_type<int> variable;
template <typename U> struct baz { my_type<U> _var_member; }

35
私はすでにこれを質問で指摘しました。私の質問は、テンプレートを使用しない場合、typedefとの違いがあるかどうかです。たとえば、「Foo foo {init_value};」を使用すると、'Foo foo(init_value)'の代わりに、両方が同じことをすることになっていますが、まったく同じルールを実行しません。したがって、/ typedefの使用と同様の隠された違いがあるかどうか疑問に思っていました。
14年

24

彼らは基本的に同じですが、using提供してalias templates非常に便利です。私が見つけることができる1つの良い例は次のとおりです。

namespace std {
 template<typename T> using add_const_t = typename add_const<T>::type;
}

したがって、std::add_const_t<T>代わりに使用できますtypename std::add_const<T>::type


私の知る限りでは、それはstd名前空間内の何も追加するには、未定義の動作です
someonewithpc

5
@someonewithpc何も追加しませんでした。すでに存在しています。タイプ名の使用例を示しただけです。en.cppreference.com/w/cpp/types/add_cvを
Validus Oculus

9

私は元のポスターが素晴らしい答えを持っていることを知っていますが、私が持っているようにこのスレッドにつまずく人のために、ここでの議論に価値のあるものを追加すると思う提案から重要な注記があります、特にtypedefキーワードが将来的に非推奨としてマークされるか、冗長/古いため削除されます:

キーワードtypedef ...を(再)使用して、テンプレートエイリアスを導入することが推奨されています。

template<class T>
  typedef std::vector<T, MyAllocator<T> > Vec;

その表記法には、型エイリアスを導入するためにすでに知られているキーワードを使用するという利点があります。ただし、いくつかの欠点[sic]も表示されます。その中に、エイリアスがタイプではなくテンプレートを指定するコンテキストで、タイプ名のエイリアスを導入することがわかっているキーワードを使用することの混乱があります。Vecは型のエイリアスではないため、typedef-nameには使用しないでください。名前Vecはファミリーの名前です。std::vector<•, MyAllocator<•> >箇条書きはタイプ名のプレースホルダーです。そのため、「typedef」構文は提案していません。一方、文は

template<class T>
  using Vec = std::vector<T, MyAllocator<T> >;

次のように読み取り/解釈できます。今後は、の同義語として使用Vec<T>しますstd::vector<T, MyAllocator<T> >。これを読むと、エイリアシングの新しい構文は合理的に論理的に思えます。

私にとって、これtypedefはC ++ でのキーワードの継続的なサポートを意味します。コードをより読みやすく、理解しやすくするためです。です。

usingキーワードの更新は特にテンプレート用であり、(承認された回答で指摘されているように)非テンプレートusingを使用しtypedefていて機械的に同一である場合、読みやすさと意図の伝達を理由にプログラマーが完全に選択できます。 。


2

どちらのキーワードも同等ですが、注意点がいくつかあります。1つは、での関数ポインターの宣言がでの宣言using T = int (*)(int, int);よりも明確であることtypedef int (*T)(int, int);です。2つ目は、テンプレートエイリアス形式はで使用できないことtypedefです。3つ目は、C APIをtypedef公開するにはパブリックヘッダーで必要になるということです。


0

Typedef宣言は初期化ステートメントとして使用できますが、エイリアス宣言は使用できません

しかし、最初の2つの非テンプレートの例では、標準に他の微妙な違いがありますか?

コーナーケースですが、typedef宣言initステートメントなので、初期化ステートメントを許可するコンテキストで使用できます。

// C++11 (C++03) (init. statement in for loop iteration statements).
for(typedef int Foo; Foo{} != 0;) {}

// C++17 (if and switch initialization statements).
if (typedef int Foo; true) { (void)Foo{}; }
//  ^^^^^^^^^^^^^^^ init-statement

switch(typedef int Foo; 0) { case 0: (void)Foo{}; }
//     ^^^^^^^^^^^^^^^ init-statement

// C++20 (range-based for loop initialization statements).
std::vector<int> v{1, 2, 3};
for(typedef int Foo; Foo f : v) { (void)f; }
//  ^^^^^^^^^^^^^^^ init-statement

一方、エイリアス宣言init-statementではないため、初期化ステートメントを許可するコンテキストでは使用できません

// C++ 11.
for(using Foo = int; Foo{} != 0;) {}
//  ^^^^^^^^^^^^^^^ error: expected expression

// C++17 (initialization expressions in switch and if statements).
if (using Foo = int; true) { (void)Foo{}; }
//  ^^^^^^^^^^^^^^^ error: expected expression

switch(using Foo = int; 0) { case 0: (void)Foo{}; }
//     ^^^^^^^^^^^^^^^ error: expected expression

// C++20 (range-based for loop initialization statements).
std::vector<int> v{1, 2, 3};
for(using Foo = int; Foo f : v) { (void)f; }
//  ^^^^^^^^^^^^^^^ error: expected expression
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.