Both the keywords can define new types, and both are equally good in standard C++. However, the main difference between using and typedef in C++ specifically relates to templates. Whenever, the program uses templates, the using directive has a clear edge over typedefs declaration. Using is also called alias declaration.
The following simple example illustrates how to use both directives. This program defines same function pointer type in 2 ways. There are no templates here, hence, both directive look similar.
#include <iostream> //main header
using namespace std; //namespace
void funda(int xvar)
{
cout << "xvar=" << xvar << endl;
}
typedef void (*fp)(int); //type with typedef
using fa = void (*)(int); //type with using
int main()
{
fp f1 = funda; //typedef type assignment
f1(4);
fa f2 = funda; //alias type assignment
f2(5);
return 0;
}
The output is similar in both cases.
The “using” declaration creates templatized declarations
In above example, “fun” was a normal function. However, when “fun” becomes a template function, things go interesting. Firstly, a typedef cannot define function pointer type because, it is not a single function but a family of functions.
template<typename T>
void funda(T xvar)
{
cout << "xvar=" << xvar << endl;
}
In this case, a similar “typedef” declaration may look like as follows. We are trying to create a “templatized” type for pointer to function.
template<typename T>
typedef void (*fp)(T);
The compiler shall generate following error:
With Using declaration
The “using” creates a “templatized” type in same case.
#include <iostream> //main header
using namespace std; //namespace
template<typename T>
void funda(T xvar)
{
cout << "xvar=" << xvar << endl;
}
template<typename T>
using fa = void (*)(T); //fa is templatized type
int main()
{
fa<int> f2 = funda; //fa initialization with int
f2(5);
fa<float> f3 = funda; //fa initialization with float
f3(5.2);
return 0;
}
Output:
“Dependent scope” must be used with “typename” keyword
We have seen “Dependent type”. Now, a “Dependent scope” is template where this type is defined. In the following example, A<R>::internal_type is dependent type and this “A<R>” is corresponding dependent scope. Any use of such “dependent type” must precede “typename” keyword. This is necessary to ensure compiler that we know it is actually a type and not something else.
#include <iostream> //main header
#include <vector> //for vector stl
using namespace std; //namespace
template<typename T>
class A
{
T val;
public:
typedef vector<T> internal_type;
};
template<typename R>
void funda()
{
A<R>::internal_type x; //No typename keyword
};
int main()
{
return 0;
}
The compiler shall generate an error when “typename” is not present.
In above example, internal_type is a type which typedef creates.
template<typename T>
class A
{
T val;
public:
typedef vector<T> internal_type; //This is type
};
However, in one of its instantiation (like, an integer-based instance) of template class, the internal_type may be a vector object and not a type.
template<>
class A<int>
{
int val;
public:
vector<int> internal_type; //this is object, not type
};
When the program uses this integer-based instantiation, then internal_type cannot be used as data type.
#include //main header
#include //for vector stl
using namespace std; //namespace
template
class A
{
T val;
public:
typedef vector internal_type; //This is type
};
template<>
class A
{
int val;
public:
vector internal_type; //this is object, not type
};
template
void funda()
{
A<int>::internal_type xvar; //This is wrong
A<float>::internal_type yvar; //This is right
};
int main()
{
return 0;
}
The compiler also shall generate error.
Same behavior with Using directive
In following example, uses “using” directive to declares type but this also has same limitation as with typedef.
template<typename T>
class A
{
T val;
public:
using internal_type = vector<T>; //same limitation as typedef
};