1095

我知道在 C++11 中我们现在可以using用来写类型别名,比如typedefs:

typedef int MyInt;

据我了解,相当于:

using MyInt = int;

新语法源于努力表达“模板类型定义”:

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

但是,对于前两个非模板示例,标准中是否还有其他细微差别?例如,typedefs 以“弱”的方式进行混叠。也就是说,它不会创建新类型,而只会创建一个新名称(这些名称之间的转换是隐含的)。

它是相同的using还是生成一个新的类型?有什么不同吗?

4

8 回答 8

632

它们是等效的,来自标准(强调我的)(7.1.3.2):

typedef-name 也可以由别名声明引入。using 关键字之后的标识符成为 typedef-name,标识符之后的可选属性说明符序列属于该 typedef-name。它具有与由 typedef 说明符引入的语义相同的语义。特别是,它没有定义新的类型,并且它不应出现在 type-id 中。

于 2012-05-25T03:16:42.650 回答
311

它们基本相同,除了:

别名声明与模板兼容,而 C 风格的 typedef 则不兼容。

于 2015-10-05T22:58:29.870 回答
218

using语法在模板中使用时具有优势。如果需要类型抽象,还需要保留模板参数以便以后可以指定。你应该写这样的东西。

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 月 Kona 后工作草案/C++17 DIS


Typedef 声明可以用作初始化语句,而别名声明不能(+)用作初始化语句

但是,对于前两个非模板示例,标准中是否还有其他细微差别?

  • 语义差异:无。
  • 允许的上下文中的差异:一些(++)

(+) P2360R0 ( Extend init-statement to allow alias-declaration )已被 CWG 批准,从 C++23 开始,typedef 声明和别名声明之间的这种不一致将被删除。
(++) 除了别名模板的例子,在原帖中已经提到过。

相同的语义

[dcl.typedef]/2 [摘录,强调我的]管理

[dcl.typedef]/2 typedef -name 也可以通过 alias-declaration引入。关键字后面的标识符成为usingtypedef -name ,标识符后面的可选属性说明序列属于该typedef-name这样的 typedef-name具有相同的语义,就好像它是由说明typedef符引入的一样。[...]

别名声明引入的typedef-name与由声明引入的语义相同typedef

允许的上下文中的细微差别

然而,这并不意味着这两种变体对于它们可能被使用的上下文具有相同的限制。事实上,尽管是一个极端情况,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;
}

别名声明不是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
于 2020-06-04T13:50:18.650 回答
25

它们本质上是相同的,但using提供了alias templates非常有用的功能。我能找到的一个很好的例子如下:

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> >. 通过这种阅读,别名的新语法似乎是合乎逻辑的。

对我来说,这意味着继续支持typedefC++ 中的关键字,因为它仍然可以使代码更具可读性和可理解性

更新using关键字是专门针对模板的,并且(正如在接受的答案中指出的那样)当您使用非模板using并且typedef机械相同时,因此基于可读性和意图交流的选择完全取决于程序员.

于 2019-06-12T02:59:59.027 回答
11

这两个关键字是等效的,但有一些注意事项。一是声明函数指针 withusing T = int (*)(int, int);比 with 更清晰typedef int (*T)(int, int);。其次是模板别名形式是不可能的typedef。第三是公开 C API 需要typedef在公共标头中。

于 2019-12-02T22:23:42.367 回答
2

截至目前,C++23 将越来越typedef紧密usingP2360建议using构成一个init 语句,例如@dfrib的答案中列出的那些error: expected expression.

但是,即使使用 P2360,atypedef也不能是模板。

总的来说,using严格来说比 更强大typedef,而且 IMO 也更具可读性。

于 2022-01-27T09:52:14.023 回答