I know that in C++11 we can now use using
to write type alias, like typedef
s:
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, typedef
s 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?
typedef void (&MyFunc)(int,int);
or using MyFunc = void(int,int);
?
typedef void MyFunc(int,int);
(which actually doesn't look as bad), or using MyFunc = void(&)(int,int);
using MyFunc = void(&)(int,int);
? does it mean MyFunc
is a reference to a function? what if you omit the &?
typedef void (&MyFunc)(int,int);
. If you omit the &
it's equivalent to typedef void MyFunc(int,int);
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.
They are largely the same, except that:
The alias declaration is compatible with templates, whereas the C style typedef is not.
typedef
if I may ask?
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; }
All standard references below refers to N4659: March 2017 post-Kona working draft/C++17 DIS.
Typedef declarations can, whereas 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: some(++).
(+) 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.
(++) In addition to the examples of alias templates, which has already been mentioned in the original post.
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 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
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.)
P
part starting with the second argument as it’s assumed; structured binding declaration to assign to x and y.
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
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
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.
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.
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-03-17] I found out today that in C++11 and C++14, typedef
cannot use an exception specification for defining an alias of a function pointer or function reference or member function pointer type, but using
can.[/Edit]
In total, using
is strictly more powerful than typedef
, and IMO more readable, too.
typedef is used for types, but it's not applicable when it comes to templates. For example:
template<typename T, typename U>
class MagicType {
public:
T dark;
U magic;
};
// not allowed
template<typename T>
typedef MagicType<std::vector<T>, std::string> FakeDarkMagic;
In the above case, you should use using instead of typedef.
Success story sharing
using
keyword seems to be a superset oftypedef
. Then, willtypdef
be deprecated in future ?using
syntax precisely because thetypedef
semantics didn't work well with templates. Which somehow contradicts the fact thatusing
is defined to have exactly the same semantics.n1489
. A template alias is not an alias for a type, but an alias for a group of templates. To make a distinction betweentypedef
the felt a need for new syntax. Also, keep in mind the OP's question is about the difference between non-template versions.typdef
being deprecated ever.