在C++ 11中'typedef'和'using'有什么区别?

Kla*_*aim 833 c++ typedef using-declaration c++11

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

typedef int MyInt;
Run Code Online (Sandbox Code Playgroud)

从我的理解,相当于:

using MyInt = int;
Run Code Online (Sandbox Code Playgroud)

这种新语法来自于努力表达" template typedef":

template< class T > using MyType = AnotherType< T, MyAllocatorType >;
Run Code Online (Sandbox Code Playgroud)

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

它是否与using生成新类型相同或是否生成新类型?有什么不同吗?

Jes*_*ood 538

它们与标准(强调我的)(7.1.3.2)相同:

也可以通过别名声明引入typedef-name.using关键字后面的标识符变为typedef-name,并且该标识符后面的可选attribute-specifier-seq属于该typedef-name.它具有与typedef说明符引入的语义相同的语义.特别是,它没有定义新类型,它不应出现在type-id中.

  • 弃用不一定表示要删除的意图 - 它仅仅是一种非常强烈的建议,希望采用其他方式. (44认同)
  • 从答案来看,`using`关键字似乎是`typedef`的超集.那么,`typdef`将来会被弃用吗? (24认同)
  • 但后来我想知道为什么他们不只是允许typedef被模板化.我记得在某个地方读过他们引入了`using`语法,因为`typedef`语义不适用于模板.这与"使用"被定义为具有完全相同的语义的事实相矛盾. (18认同)
  • @celtschk:原因在提案"n1489"中被讨论过.模板别名**不是**类型的别名,而是一组模板的别名.为了区分`typedef`,我觉得需要新的语法.另外,请记住OP的问题是关于非模板版本之间的区别. (11认同)
  • @iammilind可能不是,但你可以像编码一样编码. (7认同)
  • 那么为什么会引入这种冗余呢?2个语法用于相同的目的.而且我也没有看到`typdef`被弃用了. (4认同)
  • @iammilind`typedef`不太可能灭绝.例如,当前[标准库设计指南](http://isocpp.org/std/library-design-guidelines)有一个子句"可以使用C++ 2003语法,它们优于C++ 2011特性".当然,在用户代码中没有这样的限制. (3认同)

小智 202

它们大致相同,除了:

别名声明与模板兼容,而C样式typedef则不兼容.

  • 特别喜欢答案的简单性并指出排版的起源. (26认同)
  • @g24l你的意思是typedef..._可能_ (2认同)
  • 但这并没有回答问题。我已经知道这种差异并在原始帖子中指出了。我只是问不使用模板的情况,是否有差异。 (2认同)
  • 删除“很大程度上”一词会使这一点更清楚 (2认同)

4xy*_*4xy 192

在模板中使用时,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; }
Run Code Online (Sandbox Code Playgroud)

但是使用语法简化了这个用例.

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

my_type<int> variable;
template <typename U> struct baz { my_type<U> _var_member; }
Run Code Online (Sandbox Code Playgroud)

  • 我已经在问题中指出了这一点.我的问题是如果你不使用模板与typedef有任何区别.例如,当您使用'Foo foo {init_value};'时 而不是'Foo foo(init_value)'都应该做同样的事情,但不要完全相同的规则.所以我想知道使用/ typedef是否存在类似的隐藏区别. (35认同)

dfr*_*fri 89

以下所有标准参考均指N4659:2017 年 3 月后 Kona 工作草案/C++17 DIS


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

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

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

(1)除了别名模板的例子,在原帖中已经提到过。

相同的语义

[dcl.typedef]/2 [extract,强调我的]管辖

[dcl.typedef] / 2typedef的名称 ,也可以通过引入的 别名声明。的标识符下面的using关键字成为 typedef的名称和可选属性说明符-SEQ标识符appertains到的typedef名这样的 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;) {}

// 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

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

而一个别名声明不是一个初始化语句,并且因此可能不会在环境中使用,它允许初始化语句

// 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
Run Code Online (Sandbox Code Playgroud)

  • 哇,我的直觉最后是对的!它们是有区别的!感谢您发现这种差异,这是一种非常非常狭窄的细节,可以对我使用的代码类型产生影响(不幸的是 XD)。 (8认同)
  • 我从未在任何地方见过 typedef 的这种用法。从什么时候开始允许这样做?它可能的使用模式是什么?对我来说看起来有点可怕…… (4认同)
  • @SouravKannanthaB 从 {…} 构建 std::initializer_list ;跳过假定的从第二个参数开始的“P”部分;分配给 x 和 y 的结构化绑定声明。 (2认同)

Val*_*lus 24

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

namespace std {
 template<typename T> using add_const_t = typename add_const<T>::type;
}
Run Code Online (Sandbox Code Playgroud)

所以,我们可以用std::add_const_t<T>而不是typename std::add_const<T>::type

  • @someonewithpc我没有添加任何东西,它已经存在,我只是显示一个使用类型名的示例。请检查https://en.cppreference.com/w/cpp/types/add_cv (3认同)

Rob*_*est 14

我知道原始海报有一个很好的答案,但是对于像我这样在这个线程上磕磕绊绊的人来说,提案中有一个重要的说明,我认为它为这里的讨论增加了一些价值,尤其是评论中关于typedef关键字是否为将来会被标记为已弃用,或因冗余/旧而被删除:

有人建议(重新)使用关键字 typedef ... 来引入模板别名:

template<class T>
  typedef std::vector<T, MyAllocator<T> > Vec;
Run Code Online (Sandbox Code Playgroud)

这种表示法的优点是使用已知的关键字来引入类型别名。然而,它也显示了几个缺点 [原文如此],其中在别名不指定类型而是模板的上下文中使用已知的关键字为类型名称引入别名的混淆;Vec不是一个类型的别名,不应该采取一个typedef名。名称Vec是家族的名称std::vector<•, MyAllocator<•> >——其中项目符号是类型名称的占位符。因此,我们不建议使用“typedef”语法。另一方面,句子

template<class T>
  using Vec = std::vector<T, MyAllocator<T> >;
Run Code Online (Sandbox Code Playgroud)

可以阅读/解释为:从现在开始,我将Vec<T>用作std::vector<T, MyAllocator<T> >. 通过这种阅读,别名的新语法似乎合乎逻辑。

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

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


小智 8

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


Bol*_*pat 8

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

然而,即使使用 P2360,a 也typedef不能成为模板。

编辑[2022-09-14]:这包含不正确的信息。)

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