I am wondering whether the following code is correct. It compiles and runs on my computer but I feel like there is a recursive dependency of ShowValueClass
type alias defined in ValueClass
. Could you please explain how the compiler was able to resolve it?
#include <iostream>
namespace tmp {
template <typename T>
struct ShowValueClass {
void ShowValue() { std::cout << T::value << std::endl; }
};
} // namespace tmp
struct ValueClass {
using ShowValueClass = tmp::ShowValueClass<ValueClass>;
static constexpr int value = 42;
};
int main() {
ValueClass::ShowValueClass s;
s.ShowValue();
return 0;
}
There is no recursion here. Your class tmp::ShowValueClass<T>
just works for any type T
having a member value
that is printable by cout
(has the right ostream& operator<<(ostream&, const T&)
defined).
The fact that you added a type alias called ShowValueClass
inside ValueClass
referring to tmp::ShowValueClass<ValueClass>
changed nothing. In that context, the class acts just like a namespace.
Think about what would happen if you extracted ShowValueClass
from ValueClass
:
struct ValueClass {
static constexpr int value = 42;
};
using ShowValueClass = tmp::ShowValueClass<ValueClass>;
In that case you would accessed ShowValueClass
directly in main()
instead of prefixing it with ValueClass::
, as in your case. Both the facts that tmp::ShowValueClass
uses T::value
and that the type alias ShowValueClass
is in ValueClass
are irrelevant (there's nothing special about them).
The whole idea of using a class itself as a template parameter of a template class is widespread in C++. Actually, there is pattern called CRTP (Curiously Recurring Template Pattern) where a class inherits from template class using as parameter the class itself like (from Wikipedia):
// The Curiously Recurring Template Pattern (CRTP)
template<class T>
class Base
{
// methods within Base can use template to access members of Derived
};
class Derived : public Base<Derived>
{
// ...
};
You can check the whole article here: https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
Now, just to complete the picture, I can show you some code that won't compile because of a recursive definition:
template <typename T>
struct A {
static constexpr int val = T::val;
};
struct B {
static constexpr int val = A<B>::val;
};
int main() { }
Here, A<T>::val
depends on T::val
and that's OK, but B::val
depends on A<B>::val
which expands to B::val
. In order words, B::val
depends on B::val
, which clearly cannot be resolved. It's like saying that:
x := y(x)
with y(x)
being:
y(x) := x.
Therefore, x := x
. Clearly, there is no way to determine a value for x
.
Now, if the recursion is properly made and there is a base case defined, clearly it can be used even to compute pretty complex things. Here's a trivial example:
#include <iostream>
using namespace std;
template <int N>
struct A {
static constexpr int val = N + A<N - 1>::val;
};
template <>
struct A<0> {
static constexpr int val = 0;
};
int main() {
cout << A<10>::val << endl;
}
A<N>::val
is defined recursively as:
N + A<N-1>::val if N != 0
0 if N == 0
And, as a result, it is the sum of all the numbers from 0
to N
(inclusive).