1095

C ++ 11では、次のusingような型エイリアスの記述に使用できるようになりましたtypedef

typedef int MyInt;

私が理解していることから、次のようになります。

using MyInt = int;

そして、その新しい構文は、「テンプレートtypedef」を表現する方法を持つ努力から生まれました。

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

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

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

4

8 に答える 8

632

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

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

于 2012-05-25T03:16:42.650 に答える
311

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

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

于 2015-10-05T22:58:29.870 に答える
218

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

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; }
于 2014-04-21T11:39:59.273 に答える
115

以下のすべての標準参照は、N4659:2017年3月のコナ後の作業ドラフト/ C ++17DISを参照しています。


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

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

  • セマンティクスの違い:なし。
  • 許可されるコンテキストの違い:いくつか(++)

(+)P2360R0エイリアス宣言を許可するようにinitステートメントを拡張するはCWGによって承認されており、C ++ 23以降、typedef宣言とエイリアス宣言の間のこの不整合は削除されます。 (++)元の投稿ですでに言及されているエイリアステンプレート
の例に加えて。

同じセマンティクス

[dcl.typedef] / 2 [抽出、強調鉱山]によって管理されます

[dcl.typedef] / 2 typedef-nameは、 alias-declarationによって導入する こともできます 。キーワードに続く識別子はtypedef-nameになり、 識別子続くオプションattribute - specifier-seqはそのtypedef-nameに関連します。このような typedef-nameは、指定者によって導入された場合と同じセマンティクスを持ちます。[...]usingtypedef

alias-declarationによって導入されたtypedef-nameは、宣言によって導入された場合と同じセマンティクスを持ちます。typedef

許可されたコンテキストの微妙な違い

ただし、これは、2つのバリエーションが使用されるコンテキストに関して同じ制限があることを意味するものではありませ。実際、コーナーケースではありますが、typedef宣言initステートメントであるため、初期化ステートメントを許可するコンテキストで使用できます。

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

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

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

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

for (typedef struct { int x; int y;} P; auto [x, y] : {P{1, 1}, {1, 2}, {3, 5}})
//   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ init-statement
{
    (void)x;
    (void)y;
}

一方、alias-declarationinit-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
于 2020-06-04T13:50:18.650 に答える
25

それらは本質的に同じですが、非常に便利なものを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

于 2018-03-31T08:20:30.557 に答える
16

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

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

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

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

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

次のように読み取る/解釈できます。これからはVec<T>、の同義語として使用しstd::vector<T, MyAllocator<T> >ます。その読み方からすると、エイリアシングの新しい構文はかなり論理的であるように思われます。

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

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

于 2019-06-12T02:59:59.027 に答える
11

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

于 2019-12-02T22:23:42.367 に答える
2

今のところ、C ++ 23は近づきtypedefusing近づいています。P2360は、 @dfribの回答に。としてリストされているようなinitステートメントusingを構成することを提案しています。error: expected expression

ただし、P2360を使用しても、typedefaをテンプレートにすることはできません。

全体として、はよりusingも厳密に強力でtypedefあり、IMOも読みやすくなっています。

于 2022-01-27T09:52:14.023 に答える