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

typedef int MyInt;

在我看来,相当于:

using MyInt = int;

新语法的出现是为了有一种表达“template typedef”的方法:

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

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

它和使用是一样的还是生成一个新的类型?有什么不同吗?


当前回答

它们是等价的,从标准(强调我的)(7.1.3.2):

typef -name也可以通过别名声明引入。的 using关键字后面的标识符变成了typedef-name,而 可选的属性说明符-seq跟在标识符后面 到那个typef -name。它有相同的语义,就好像它是 由typedef说明符引入。特别是,它 没有定义新类型,并且它不应该出现在type-id中。

其他回答

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; }

以下所有标准参考资料均参考N4659: 2017年3月后kona工作草案/ c++ 17 DIS。


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

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

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

(+) P2360R0(扩展初始化语句以允许别名声明)已经被CWG批准,从c++ 23开始,类型定义声明和别名声明之间的不一致将被消除。 (++)除了别名模板的例子,这在原来的帖子中已经提到过。

相同的语义

由[dcl.]管理。/2[提取,强调矿山]

(dcl。typedef) / 2 类型名 还可以介绍一个 alias-declaration。 using关键字后面的标识符变成a typef -name和标识符后面的可选属性说明符-seq都属于该typef -name。这样一个 typepedef -name具有与typedef说明符引入的相同的语义。[…]

由别名声明引入的typepedef -name与由typedef声明引入的typef -name具有相同的语义。

允许上下文的细微差别

然而,这并不意味着这两种变体在使用上下文方面有相同的限制。实际上,尽管是极端情况,typedef声明是一个初始化语句,因此可以在允许初始化语句的上下文中使用

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

而别名声明不是初始化语句,因此不能在允许初始化语句的上下文中使用

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

它们在很大程度上是相同的,除了:

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

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

它们是等价的,从标准(强调我的)(7.1.3.2):

typef -name也可以通过别名声明引入。的 using关键字后面的标识符变成了typedef-name,而 可选的属性说明符-seq跟在标识符后面 到那个typef -name。它有相同的语义,就好像它是 由typedef说明符引入。特别是,它 没有定义新类型,并且它不应该出现在type-id中。