What is the logic behind the "using" keyword in C++?
C++C++11C++ Problem Overview
What is the logic behind the "using" keyword in C++?
It is used in different situations and I am trying to find if all those have something in common and there is a reason why the "using" keyword is used as such.
using namespace std; // to import namespace in the current namespace
using T = int; // type alias
using SuperClass::X; // using super class methods in derived class
C++ Solutions
Solution 1 - C++
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
> template
> template
> struct Vec {
> typedef MyVector::type
> We’d prefer something like the following:
>
> template Vec<T>
in deducible context, and the syntax is
> improved as well. For example we could rewrite foo as:
>
> template 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:
>
> templateVec
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
>
> templateVec<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
andusing 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);
Solution 2 - C++
Another difference between typedef and using: you may do this:
using vector3d_t = double[3];
vector3d_t v = {1,2,3};
v[1] = 4;
It is not possible with typedef:
typedef double[3] vector3d_t; // Compilation error