What is the difference between 'typedef' and 'using'?
Asked Answered
Z

8

1258

I know that in C++11 we can now use 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< class T > using MyType = AnotherType< T, MyAllocatorType >;

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?

Zwart answered 25/5, 2012 at 2:39 Comment(13)
I personally prefer the new syntax because it is much more similar to regular variable assignment, improving readability. For example, do you prefer typedef void (&MyFunc)(int,int); or using MyFunc = void(int,int); ?Nebulosity
I fully agree, I only use the new syntax now. That's why I was asking, to be sure there is really no difference.Zwart
@MatthieuM. those two are different btw. It should be typedef void MyFunc(int,int); (which actually doesn't look as bad), or using MyFunc = void(&)(int,int);Sabatier
@R.MartinhoFernandes: Ah thanks, I did not know of the first form and thought it was equal to the using form I presented. Indeed the first is not too bad (except for having the name in the middle).Nebulosity
@R.MartinhoFernandes why do you need (&) in using MyFunc = void(&)(int,int); ? does it mean MyFunc is a reference to a function? what if you omit the &?Widner
Yes, it's a function reference. It's equivalent to typedef void (&MyFunc)(int,int);. If you omit the & it's equivalent to typedef void MyFunc(int,int);Sabatier
In this episode of CppCast Edouard Alligand claims that using results in faster link times (don't remember which compiler he was talking about, though), because apparently the compiler generates shorter symbol names. I don't know if this holds up to scrutiny, though.Assessment
Irrespective of the actual answer: thank you @Zwart for helping me with the template syntax - your naming convention is much clearer than the standard code snippets.Marivaux
@Marivaux Note that both notations are standard, but the first one is "legacy".Zwart
I don't think this warrants a separate question, but what does this look like using using? struct Private; typedef int(Private::*Zero); (Found in Qt's QFlags.h.) I simply cannot make sense of the syntax.Edgy
The core guidlines suggest using over typedef for defining aliasesEmcee
I changed the accepted answer because someone finally found a tiny difference that can be noticed in some ... "advanced" kind of codebases. Thanks @dfriZwart
using is C++ only, typedef can be used in C also.Devol
A
166

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?

  • 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 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.

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

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<int> v{1, 2, 3};
for (using Foo = int; Foo f : v) { (void)f; }
//   ^^^^^^^^^^^^^^^ error: expected expression
Annabellannabella answered 4/6, 2020 at 13:50 Comment(10)
Wow, my intuition was right in the end! There is a difference! Thanks for finding that difference, it's the kind of very very narrow detail that can make a difference in the kind of code I work with (unfortunately XD).Zwart
I've never seen this usage of typedef anywhere. Since when is this allowed and what are possible usage patterns of this? Looks kind of horrible to me tbh...Lexie
auto [x, y] : {P{1, 1}, {1, 2}, {3, 5}}) { (void)x; (void)y; } What is this called? Where can I find more on this? (Btw, it has one missing open parenthesis.)Christman
Is there template - typedef? similar to template - using?Christman
@SouravKannanthaB Construction of an std::initializer_list from {…}; skipping the P part starting with the second argument as it’s assumed; structured binding declaration to assign to x and y.Giantism
@SouravKannanthaB the opening parenthesis is on the previous line.Marmalade
@SouravKannanthaB, as pointed out by @4xy's answer, there is no such thing as a template typedef, and there is no such thing planned in the near future (C++23).Billi
@RichardVodden Indeed. Note that this is mentioned already in the answer (see (+)).Annabellannabella
A c++23 the alias declaration with using in an init statement will be allowed as well (you can try on coliru) So then this difference between typedef and using is also gone.Huan
@Huan Yes, this is mentioned in the answer, see (+). I'll can update the title to make it more clear as others have missed this also. It's likely that this answer prompted P2360R0.Annabellannabella
K
703

They are equivalent, from the standard (emphasis mine) (7.1.3.2):

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. It has the same semantics as if it were introduced by the typedef specifier. In particular, it does not define a new type and it shall not appear in the type-id.

Kellby answered 25/5, 2012 at 3:16 Comment(10)
From the answer, using keyword seems to be a superset of typedef. Then, will typdef be deprecated in future ?Ev
@Ev I don't think so as it is part of C and certainly will not be removed from it. Also, C++11 and future versions will still keep maximum compatibility with our current code...Zwart
Deprecation doesn't necessarily indicate intent to remove--It merely stands as a very strong recommendation to prefer another means.Sunglasses
@Ev typedef is unlikely to become extinct. For one, the current Standard Library design guidelines have a clause "where C++ 2003 syntax can be used, they are preferred over C++ 2011 features". Of course, in user code no such restrictions apply.Transvestite
But then I wonder why they didn't just allow typedef to be templated. I remember having read somewhere that they introduced the using syntax precisely because the typedef semantics didn't work well with templates. Which somehow contradicts the fact that using is defined to have exactly the same semantics.Douville
@celtschk: The reason is talked about in the proposal n1489. A template alias is not an alias for a type, but an alias for a group of templates. To make a distinction between typedef the felt a need for new syntax. Also, keep in mind the OP's question is about the difference between non-template versions.Kellby
What about with template-templates? A typedef for one of the template template parameters doesn't appear to be suitable as the typedef isn't correct unless it itself has template parameters specified. Can the type alias be used here, or is it equivalent to a typedef in this case (and can't be used)? Or is the solution to this a template type alias? That's a difference between a type alias and a typedef then, yes?Polynuclear
So why this redundancy whas introduced ? 2 syntaxes for the same purpose. And I don't see typdef being deprecated ever.Norinenorita
I have had an experience which using was faster than typedef.Rossetti
In theory, they're the same except that with using, you can use templates, but you cannot with typedef.Fascine
C
422

They are largely the same, except that:

The alias declaration is compatible with templates, whereas the C style typedef is not.

Cauterant answered 5/10, 2015 at 22:58 Comment(6)
Particularly fond of the simplicity of the answer and pointing out the origin of typeset.Mvd
@Mvd you mean typedef... probablyBriggs
What is the difference between C and C++ in typedef if I may ask?Penates
This is not answering the question though. I already know that difference and pointed at in the original post. I was asking only about the case where you don't use templates, is there differences.Zwart
removing the word 'largely ' would make this clearerSherwood
@McSinyx, there is no difference.Billi
R
234

The using syntax has an advantage when used within templates. If you need the type abstraction, but also need to keep template parameter to be possible to be specified in future. You should write something like this.

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

But using syntax simplifies this use case.

template <typename T> using my_type = whatever<T>;

my_type<int> variable;
template <typename U> struct baz { my_type<U> _var_member; }
Recluse answered 21/4, 2014 at 11:39 Comment(1)
I already pointed this in the question though. My question is about if you don't use template is there any difference with typedef. As, for example, when you use 'Foo foo{init_value};' instead of 'Foo foo(init_value)' both are supposed to do the same thing but don't foillow exactly the same rules. So I was wondering if there was a similar hidden difference with using/typedef.Zwart
A
166

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?

  • 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 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.

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

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<int> v{1, 2, 3};
for (using Foo = int; Foo f : v) { (void)f; }
//   ^^^^^^^^^^^^^^^ error: expected expression
Annabellannabella answered 4/6, 2020 at 13:50 Comment(10)
Wow, my intuition was right in the end! There is a difference! Thanks for finding that difference, it's the kind of very very narrow detail that can make a difference in the kind of code I work with (unfortunately XD).Zwart
I've never seen this usage of typedef anywhere. Since when is this allowed and what are possible usage patterns of this? Looks kind of horrible to me tbh...Lexie
auto [x, y] : {P{1, 1}, {1, 2}, {3, 5}}) { (void)x; (void)y; } What is this called? Where can I find more on this? (Btw, it has one missing open parenthesis.)Christman
Is there template - typedef? similar to template - using?Christman
@SouravKannanthaB Construction of an std::initializer_list from {…}; skipping the P part starting with the second argument as it’s assumed; structured binding declaration to assign to x and y.Giantism
@SouravKannanthaB the opening parenthesis is on the previous line.Marmalade
@SouravKannanthaB, as pointed out by @4xy's answer, there is no such thing as a template typedef, and there is no such thing planned in the near future (C++23).Billi
@RichardVodden Indeed. Note that this is mentioned already in the answer (see (+)).Annabellannabella
A c++23 the alias declaration with using in an init statement will be allowed as well (you can try on coliru) So then this difference between typedef and using is also gone.Huan
@Huan Yes, this is mentioned in the answer, see (+). I'll can update the title to make it more clear as others have missed this also. It's likely that this answer prompted P2360R0.Annabellannabella
D
29

They are essentially the same but using provides alias templates which is quite useful. One good example I could find is as follows:

namespace std {
 template<typename T> using add_const_t = typename add_const<T>::type;
}

So, we can use std::add_const_t<T> instead of typename std::add_const<T>::type

Domenech answered 31/3, 2018 at 8:20 Comment(3)
As far as I know, it's undefined behavior to add anything inside the std namespaceCoarsegrained
@Coarsegrained I was not adding anything, it already exists, I was just showing an example of typename usage. Please check en.cppreference.com/w/cpp/types/add_cvDomenech
Is the typename keyword in the RHS optional? I.e., template<typename T> using add_const_t = add_const<T>::type; seems equivalent to the above code I guess. If anyone has advice on this, any comment would be helpful.Massie
I
16

I know the original poster has a great answer, but for anyone stumbling on this thread like I have there's an important note from the proposal that I think adds something of value to the discussion here, particularly to concerns in the comments about if the typedef keyword is going to be marked as deprecated in the future, or removed for being redundant/old:

It has been suggested to (re)use the keyword typedef ... to introduce template aliases:

template<class T>
  typedef std::vector<T, MyAllocator<T> > Vec;

That notation has the advantage of using a keyword already known to introduce a type alias. However, it also displays several disavantages [sic] among which the confusion of using a keyword known to introduce an alias for a type-name in a context where the alias does not designate a type, but a template; Vec is not an alias for a type, and should not be taken for a typedef-name. The name Vec is a name for the family std::vector<•, MyAllocator<•> > – where the bullet is a placeholder for a type-name.Consequently we do not propose the “typedef” syntax.On the other hand the sentence

template<class T>
  using Vec = std::vector<T, MyAllocator<T> >;

can be read/interpreted as: from now on, I’ll be using Vec<T> as a synonym for std::vector<T, MyAllocator<T> >. With that reading, the new syntax for aliasing seems reasonably logical.

To me, this implies continued support for the typedef keyword in C++ because it can still make code more readable and understandable.

Updating the using keyword was specifically for templates, and (as was pointed out in the accepted answer) when you are working with non-templates using and typedef are mechanically identical, so the choice is totally up to the programmer on the grounds of readability and communication of intent.

Indignity answered 12/6, 2019 at 2:59 Comment(0)
M
16

Both keywords are equivalent, but there are a few caveats. One is that declaring a function pointer with using T = int (*)(int, int); is clearer than with typedef int (*T)(int, int);. Second is that template alias form is not possible with typedef. Third is that exposing C API would require typedef in public headers.

Masaccio answered 2/12, 2019 at 22:23 Comment(0)
B
9

As of now, C++23 will get typedef and using closer together: P2360 proposes that using constitute an init-statement such as the ones listed in @dfrib's answer as error: expected expression.

However, even with P2360, a typedef cannot be a template.

(Edit [2022-09-14]: This contained incorrect information.)

In total, using is strictly more powerful than typedef, and IMO more readable, too.

Billi answered 27/1, 2022 at 9:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.