我知道在C ++ 11中我们现在可以使用using
来编写类型别名,例如typedef
s:
typedef int MyInt;
根据我的理解,相当于:
using MyInt = int;
这种新语法源于努力表达" template typedef
":
template< class T > using MyType = AnotherType< T, MyAllocatorType >;
但是,对于前两个非模板示例,标准中是否还有其他细微差别?例如,typedef
s在&#34;弱&#34;中做别名办法。也就是说,它不会创建新类型,只会创建一个新名称(这些名称之间隐含了转换)。
与using
相同还是生成新类型?有什么不同吗?
答案 0 :(得分:514)
它们与标准(强调我的)(7.1.3.2)相同:
也可以通过别名声明引入typedef-name。该 using关键字后面的标识符变为typedef-name和 标识符appertains后面的可选attribute-specifier-seq 到那个typedef-name。 它具有与之相同的语义 由typedef说明符引入。特别是它 没有定义新类型,它不应出现在type-id。
中
答案 1 :(得分:184)
使用语法在模板中使用时具有优势。如果需要类型抽象,还需要保留模板参数以便将来可以指定。你应该写这样的东西。
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; }
答案 2 :(得分:174)
除了:
之外,它们基本相同别名声明与模板兼容,而C 样式typedef不是。
答案 3 :(得分:32)
以下所有标准参考文献均引用N4659: March 2017 post-Kona working draft/C++17 DIS。
但是,在前两个非模板示例中, 标准中还有其他细微的差异吗?
(1)除了别名模板的示例外,该示例已在原始帖子中提到。
受[dcl.typedef]/2的约束[摘录,强调我的]
[dcl.typedef] / 2 typedef-name 也可以由 alias-declaration。 关键字
using
之后的 identifier 变为 typedef-name 和 identifier 后的可选 attribute-specifier-seq 属于该 typedef-name 。 这样 typedef-name 具有与typedef
说明符引入的语义相同。 [...]
由别名声明引入的 typedef-name 具有相同的语义,就像typedef
引入的一样。声明。
但是,这不是并不意味着这两个变体在使用它们的上下文方面具有相同的限制。确实,尽管有一个极端的情况,typedef declaration是一个init-statement,因此可以在允许初始化语句的上下文中使用
// 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; }
alias-declaration不是初始声明,因此,不得用于允许初始化语句的上下文中
// 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
答案 4 :(得分:22)
它们基本相同,但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
答案 5 :(得分:2)
我知道原始发帖人有一个很好的答案,但是对于像我一样绊脚石的任何人,the proposal中都有一个重要提示,我认为这为此处的讨论增加了一些价值,尤其是对于关于typedef
关键字将来是否将被标记为已弃用或由于多余/旧而被删除的评论:
建议(重新)使用关键字typedef ...引入模板别名:
template<class T> typedef std::vector<T, MyAllocator<T> > Vec;
该表示法的优点是使用已知的关键字来引入类型别名。但是,它也显示了一些不足之处,其中在别名不是指定类型而是模板的情况下,使用已知为类型名称引入别名的关键字会造成混淆。
Vec
是不是类型的别名,并且不应用作typedef名称。名称Vec
是std::vector<•, MyAllocator<•> >
族的名称-项目符号是类型名称的占位符。因此,我们不建议使用“ typedef”语法。另一方面,句子>template<class T> using Vec = std::vector<T, MyAllocator<T> >;
可以理解为:从现在开始,我将使用
Vec<T>
作为std::vector<T, MyAllocator<T> >
的同义词。通过阅读,别名的新语法似乎合乎逻辑。
对我来说,这意味着将继续支持C ++中的typedef
关键字,因为它仍然可以使代码更易读和易懂。
更新using
关键字专门用于模板,并且(如接受的答案所指出),当您使用非模板时,using
和typedef
在机械上是相同的,因此,选择权完全取决于程序员的可读性和意图传达。
答案 6 :(得分:1)
两个关键字都是等效的,但有一些警告。一个是,使用using T = int (*)(int, int);
声明函数指针比使用typedef int (*T)(int, int);
更清晰。其次,typedef
无法使用模板别名形式。第三是公开C API将需要在公共标头中使用typedef
。