A typedef for when an alias declaration cannot

Tags// , , ,

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 the typedef 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.

  1. E.g. as in the following very highly upvoted StackOverflow answer. ↩︎

  2. All standard references in this post refers to N4659: March 2017 post-Kona working draft/C++17 DIS. ↩︎

  3. 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. ↩︎

  4. Leveraging non-deduced contexts for template argument deduction. ↩︎

More Reading
comments powered by Disqus