In C++11, the using
keyword when used for type alias
is identical to typedef
.
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.
Bjarne Stroustrup provides a practical example:
typedef void (*PFD)(double); // C style typedef to make `PFD` a pointer to a function returning void and accepting double
using PF = void (*)(double); // `using`-based equivalent of the typedef above
using P = [](double)->void; // using plus suffix return type, syntax error
using P = auto(double)->void // Fixed thanks to DyP
Pre-C++11, the using
keyword can bring member functions into scope. In C++11, you can now do this for constructors (another Bjarne Stroustrup example):
class Derived : public Base {
public:
using Base::f; // lift Base's f into Derived's scope -- works in C++98
void f(char); // provide a new f
void f(int); // prefer this f to Base::f(int)
using Base::Base; // lift Base constructors Derived's scope -- C++11 only
Derived(char); // provide a new constructor
Derived(int); // prefer this constructor to Base::Base(int)
// ...
};
Ben Voight provides a pretty good reason behind the rationale of not introducing a new keyword or new syntax. The standard wants to avoid breaking old code as much as possible. This is why in proposal documents you will see sections like Impact on the Standard
, Design decisions
, and how they might affect older code. There are situations when a proposal seems like a really good idea but might not have traction because it would be too difficult to implement, too confusing, or would contradict old code.
Here is an old paper from 2003 n1449. The rationale seems to be related to templates. Warning: there may be typos due to copying over from PDF.
First let’s consider a toy example:
template <typename T>
class MyAlloc {/*...*/};
template <typename T, class A>
class MyVector {/*...*/};
template <typename T>
struct Vec {
typedef MyVector<T, MyAlloc<T> > type;
};
Vec<int>::type p; // sample usage
The fundamental problem with this idiom, and the main motivating fact
for this proposal, is that the idiom causes the template parameters to
appear in non-deducible context. That is, it will not be possible to
call the function foo below without explicitly specifying template
arguments.
template <typename T> void foo (Vec<T>::type&);
So, the syntax is somewhat ugly. We would rather avoid the nested ::type
We’d prefer something like the following:
template <typename T>
using Vec = MyVector<T, MyAlloc<T> >; //defined in section 2 below
Vec<int> p; // sample usage
Note that we specifically avoid the term “typedef template” and
introduce the new syntax involving the pair “using” and “=” to help avoid
confusion: we are not defining any types here, we are introducing a
synonym (i.e. alias) for an abstraction of a type-id (i.e. type
expression) involving template parameters. If the template parameters
are used in deducible contexts in the type expression then whenever
the template alias is used to form a template-id, the values of the
corresponding template parameters can be deduced – more on this will
follow. In any case, it is now possible to write generic functions
which operate on Vec<T>
in deducible context, and the syntax is
improved as well. For example we could rewrite foo as:
template <typename T> void foo (Vec<T>&);
We underscore here that one of the primary reasons for proposing
template aliases was so that argument deduction and the call to foo(p)
will succeed.
The follow-up paper n1489 explains why using
instead of using typedef
:
It has been suggested to (re)use the keyword typedef — as done in the
paper [4] — 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 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< [bullet] , MyAllocator< [bullet] > >
– 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.
I think the important distinction is made here, aliases instead of types. Another quote from the same document:
An alias-declaration is a declaration, and not a definition. An alias-
declaration introduces a name into a declarative region as an alias
for the type designated by the right-hand-side of the declaration. The
core of this proposal concerns itself with type name aliases, but the
notation can obviously be generalized to provide alternate spellings
of namespace-aliasing or naming set of overloaded functions (see ?
2.3 for further discussion). [My note: That section discusses what that syntax can look like and reasons why it isn't part of the proposal.] It may be noted that the grammar production alias-declaration is acceptable anywhere a typedef
declaration or a namespace-alias-definition is acceptable.
Summary, for the role of using
:
- template aliases (or template typedefs, the former is preferred namewise)
- namespace aliases (i.e.,
namespace PO = boost::program_options
and using PO = ...
equivalent)
- the document says
A typedef declaration can be viewed as a special case of non-template alias-declaration
. It's an aesthetic change, and is considered identical in this case.
- bringing something into scope (for example,
namespace std
into the global scope), member functions, inheriting constructors
It cannot be used for:
int i;
using r = i; // compile-error
Instead do:
using r = decltype(i);
Naming a set of overloads.
// bring cos into scope
using std::cos;
// invalid syntax
using std::cos(double);
// not allowed, instead use Bjarne Stroustrup function pointer alias example
using test = std::cos(double);