gotchacppCritical
What is the difference between 'typedef' and 'using'?
Viewed 0 times
andtypedefbetweenthedifferenceusingwhat
Problem
I know that in C++11 we can now use
Is, from what I understand, equivalent to:
And that new syntax emerged from the effort to have a way to express "template typedef":
But, with the first two non-template examples, are there any other subtle differences in the standard? For example,
Is it the same with
using to write type alias, like typedefs:typedef int MyInt;Is, from what I understand, equivalent to:
using MyInt = int;And that new syntax emerged from the effort to have a way to express "template typedef":
template using MyType = AnotherType;But, with the first two non-template examples, are there any other subtle differences in the standard? For example,
typedefs do aliasing in a "weak" way. That is it does not create a new type but only a new name (conversions are implicit between those names).Is it the same with
using or does it generate a new type? Are there any differences?Solution
All standard references below refers to N4659: March 2017 post-Kona working draft/C++17 DIS.
Typedef declarations can, whereas (until C++23) alias declarations cannot, be used as initialization statements
But, with the first two non-template examples, are
there any other subtle differences in the standard?
(+) Not including the examples of alias templates, which has already been mentioned in the original post.
(++) P2360R0 (Extend init-statement to allow alias-declaration) has been approved by CWG and as of C++23, this inconsistency between typedef declarations and alias declarations will have been removed.
Same semantics
As governed by [dcl.typedef]/2 [extract, emphasis mine]
[dcl.typedef]/2 A
typedef-name
can also be introduced by an
alias-declaration.
The identifier following the
typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. Such a
typedef-name has the same semantics as if it were introduced by the
a typedef-name introduced by an alias-declaration has the same semantics as if it were introduced by the
Subtle difference in allowed contexts
However, this does not imply that the two variations have the same restrictions with regard to the contexts in which they may be used. And indeed, albeit a corner case, a typedef declaration is an init-statement and may thus be used in contexts which allow initialization statements
whereas before C++23 (this answer may have prompted P2360R0 which addressed this niche subtlety in C++23) an alias-declaration is not an init-statement, and thus may not be used in contexts which allows initialization statements
Typedef declarations can, whereas (until C++23) alias declarations cannot, be used as initialization statements
But, with the first two non-template examples, are
there any other subtle differences in the standard?
- Differences in semantics: none.
- Differences in allowed contexts(+):
- C++20 and earlier: some.
- C++23 and onwards: none(++).
(+) Not including the examples of alias templates, which has already been mentioned in the original post.
(++) P2360R0 (Extend init-statement to allow alias-declaration) has been approved by CWG and as of C++23, this inconsistency between typedef declarations and alias declarations will have been removed.
Same semantics
As governed by [dcl.typedef]/2 [extract, emphasis mine]
[dcl.typedef]/2 A
typedef-name
can also be introduced by an
alias-declaration.
The identifier following the
using keyword becomes atypedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. Such a
typedef-name has the same semantics as if it were introduced by the
typedef specifier. [...]a typedef-name introduced by an alias-declaration has the same semantics as if it were introduced by the
typedef declaration.Subtle difference in allowed contexts
However, this does not imply that the two variations have the same restrictions with regard to the contexts in which they may be used. And indeed, albeit a corner case, a typedef declaration is an init-statement and may thus be used in contexts which allow initialization statements
// 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 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;
}whereas before C++23 (this answer may have prompted P2360R0 which addressed this niche subtlety in C++23) an alias-declaration is not an init-statement, and thus may not be used in contexts which allows initialization statements
// 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 v{1, 2, 3};
for (using Foo = int; Foo f : v) { (void)f; }
// ^^^^^^^^^^^^^^^ error: expected expressionCode Snippets
// 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 expressionContext
Stack Overflow Q#10747810, score: 188
Revisions (0)
No revisions yet.