Reputation: 61
I was going through the template Aliases from Tour of C++. I couldn't understand the below code and how to use it?
template<typename T>
class Vector {
public:
using value_type = T;
}
Here he is using the value_type as type alias for typename 'T', Why can't we just use the typename T, since we can pass any type to template(i.e. class Vector). What is the need to have alias for template?
template<typename C>
using Value_type = typename C::value_type;
Here how is value_type in the scope of C, i.e. how can we reference value_type with type 'C', since it is inside class "vector"? does 'value_type' here mean its a Vector? and 'Value_type' mean int::Vector or string::Vector etc..?
template<typename Container>
void algo(Container &c)
{
Vector<Value_type<Container>> vec;
}
How are these three pieces linked?
Upvotes: 1
Views: 138
Reputation: 122458
Why a member alias?
Consider you use an instantiation of Vector
in some other generic code:
template <typename U>
void foo(const U& u);
Vector<int> v;
foo(v);
Then inside foo
we would need to go through some hoops to find out that T
is int
. foo
only knows about U
which is Vector<int>
. If Vector<int>
has a value_type
alias then it is much simpler to access that:
template <typename U>
void foo(const U& u) {
using value_type = typename U::value_type;
//...
}
Now foo
does not need to care that U::value_type
actually was the T
parameter to Vector
and is also fine with a type that is not an instantiation of a template:
struct OtherVector {
using value_type = int;
};
OtherVector ov;
foo(ov);
No member alias: "some hoops"
Using a member alias is the simple way. For the sake of completeness I want to show the complicated way that lets foo
infer T
from Vector<T>
. You'll have to bear with me...
First, note that function templates cannot be partially specialized. Thats why I introduce a level of indirection:
template <typename U>
struct foo_impl {
void operator()(const U& u) {
std::cout << "Hello \n";
}
};
template <typename U>
void foo(const U& u) { foo_impl<U>{}(u); }
Caller will call foo
and in the background we can mess around with foo_impl
.
For example we can add a partial specilization for Vector<T>
where we can directly have access to T
:
template <typename T>
struct foo_impl< Vector<T> > {
void operator()(const Vector<T>& v) {
std::cout << "Hello Vector<T>\n";
if constexpr (std::is_same_v<int,T>) {
std::cout << "T == int\n";
}
}
};
However, consider what that means for foo
. The foo
above is fine with any type that has a value_type
alias. Now we have a rather boring general definition (prints "Hello"
) and a specialization for Vector<T>
(that prints more when T==int
). Thats quite a restriction if we want foo
to also work with a
template <typename T>
struct Bar {};
What can we do? We can provide a more generic specialization that matches also instantiations of Bar
:
template <template<class> class A, class T>
struct foo_impl< A<T> > {
void operator()(const A<T>& v) {
std::cout << "Hello A<T>\n";
if constexpr (std::is_same_v<int,T>) {
std::cout << "T == int\n"; // We know what T is when a Vector<T> is passed !!
}
}
};
It is using a template tempalte parameter to match any instantiation of a template with a single template parameter.
Are we fine now? Unfortunately no, because suppose we want to get a "value type" from this:
template <typename A,typename B>
struct Moo {};
Suppose by convention the first parameter, A
, is the value type we are looking for. Then we need to add an even more generic specialization:
template <template<class...> class A, typename T,typename... Others>
struct foo_impl< A<T,Others...> > {
void operator()(const A<T,Others...>& v) {
std::cout << "Hello A<T>\n";
if constexpr (std::is_same_v<int,T>) {
std::cout << "T == int\n";
}
}
};
This will match an instantiation of a template with any number of type parameters and will detect the first template parameter to be T
.
Are we fine yet? Unfortunately: Not at all. The above does not match a
template <typename A, int x>
struct Omg {};
Because it has a non-type template parameter. We could also fix that, but lets stop here. Requiring that a value type is always the first parameter is too restrictive anyhow.
TL;DR
What we actually want is to make foo
work with any type that has an associated "value type". And the simple way to do that is that any type that should be passed to foo
to supply a member alias called value_type
.
Upvotes: 5