A typedef for when an alias declaration cannot
What is the difference between typedef declarations and alias declarations?
A common question, particularly for developers migrating from pre-C++11 projects to more modern C++, is whether there are any differences between typedef declarations and alias declarations.
// target_specific_fixed_width_types.h
typedef float Float32_t; // typedef declaration
using Float64_t = double; // alias declaration
A likewise common answer1 typically goes along the lines of
They are equivalent, but alias declarations can also be used to declare alias templates.
The latter, alias templates, as specified in [temp.alias]2 and as introduced by N14893, are commonly used e.g. to define helper types for type transformation traits; we used this in a previous post4 when declaring the helper type identity_t
, aliasing the wrapped type in the transformation trait identity
:
template<typename T>
struct identity { using type = T; }
template<typename T>
using identity_t = typename identity::type;
In this post we shall look into, for fun and probably not for too much profit, whether typedef declarations and alias declarations are—beyond the exception of alias templates—actually equivalent or not.
Same semantics
As governed by [dcl.typedef]/2 [extract, emphasis mine]:
A typedef-name can also be introduced by an alias-declaration. The identifier following the
using
keyword becomes a 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 thetypedef
specifier. […]
a typedef-name introduced by an alias-declaration has the same semantics as if it were introduced by the typedef
declaration. As such, w.r.t. semantics, we are aligned with the “common answer” phrased above.
Subtle difference in allowed contexts
However, even if the two variations have the same semantics, it does not imply that the two variations have the same restrictions w.r.t. in which contexts 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 allows initialization statements
// C++11 (C++03) (init. statement in for loop iteration statements).
for(typedef int Foo;
// ^^^^^^^^^^^^^^^ init-statement
// 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; }
whereas an alias-declaration is not an init-statement, and may thus 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<int> v{1, 2, 3};
for(using Foo = int; Foo f : v) { (void)f; }
// ^^^^^^^^^^^^^^^ error: expected expression
for(using P = struct { int x; int y; };
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression
auto [x, y] : {P{1, 1}, {1, 2}, {3, 5}}) { (void)x; (void)y; }
Whether using typedef declarations as initializations statements can ever be actually useful (beyond solving XY problems), even once in a blue moon, will be left unsaid, but we have learned about a curiosity w.r.t. initialization statements.
Summary
As shown above, beyond the special case of alias templates, the difference between typedef declarations and alias declarations can be summarized as:
- Differences in semantics: none.
- Differences in allowed contexts: some.
- Differences in useful contexts: probably none.
-
E.g. as in the following very highly upvoted StackOverflow answer. ↩︎
-
All standard references in this post refers to N4659: March 2017 post-Kona working draft/C++17 DIS. ↩︎
-
N1489 explicitly argues as for why there shall be no typedef templates, only alias templates, an essential difference as compared to the original proposal N1406: Proposed Addition to C++: Typedef Templates from Herb. ↩︎
-
Leveraging non-deduced contexts for template argument deduction. ↩︎