The template declaration you provided take a so called template-template parameter (a template parameter of template type). Yet the example of usage you provided attempts to pass an "ordinary" class as an argument (once all template parameters are fixed, template class turns into an "ordinary" class, it is no longer a template).
This immediately means that template-template parameter is not what you need. Template-template parameters serve a completely different purpose. (I won't go into details here).
One possible solution for your problem is to require the argument classes to expose their template arguments through nested types and constants. I.e. your hello
template must contain a nested constant beta_value
and nested typename gamma_type
template <int BETA, typename GAMMA> class hello
{
public:
static const int beta_value = BETA;
typedef GAMMA gamma_type;
...
};
In this case your function will be declared with ordinary type template parameter
template <typename ALPHA> typename ALPHA::gamma_type foo()
{
// do stuff with beta, gamma
typename ALPHA::gamma_type c[ALPHA::beta_value];
ALPHA a();
ALPHA b();
}
If some user forget to follow the convention, the compiler will refuse to compile foo
and force that user to update the definition of their argument class.