*shrug*
I don't know of any compiler that fully supports template methods where this will not work.
This only relies on the compiler picking the correct overload. If a compiler doesn't pick the correct overload here it probably never does.
Without having a full example I can't be sure, but you probably do not need the 'if' statement at all because you do not need it to eliminate the recursion. The general case, the one overload, always recurses down and the terminating case, the other overload, doesn't.
In the interest of... the interesting, there are a few compilers around that do have problems with template methods that will not handle this situation. You will probably never run into them because the latest versions of all the popular compilers work correctly for this example. If supporting older or simply odd compilers is important, you can increase the portability by providing an interface that makes the choice of overloads explicit through reliance on non-template functions.
Just out of curiosity, do you want to allow any 'void cmatrix<?, ?, ?>::mgso(cupper<?, ?> &)' to be called with any 'cupper<?, ?>' reference? Or should the column size match? (Should the '$' representing 'ucols' and ncols' match in 'void cmatrix<?, $, ?>::mgso(cupper<$, ?> &)'.)
I've included some source which should explain virtually everything.
Soma
Code:
#include <iostream>
struct false_type{};
struct true_type{};
template
<
unsigned int value_F
>
struct value_type{};
// the value is never used
// types distinguish the result
// everything matches the general case
const false_type * is_value_one(...){return(0);}
// but only a paramter convertble to a 'const value_type<1> &' matches here
const true_type * is_value_one(const value_type<1> &){return(0);}
template
<
unsigned int a_F,
unsigned int b_F
>
struct test1_p
{
static const unsigned int a = a_F;
static const unsigned int b = b_F;
};
template
<
unsigned int a_F,
unsigned int c_F,
unsigned int d_F
>
struct tester1
{
template
<
unsigned int a_FN,
unsigned int b_FN
>
void test
(
const test1_p<a_FN, b_FN> & f
) // general case: always recurse
{
tester1<a_FN, c_F - 1, d_F> t1;
test1_p<a_FN, b_FN - 1> t1p;
std::cout
<< '(' << a_F << ':' << c_F << ':' << d_F << ')'
<< "::" << '(' << a_FN << ':' << b_FN << ')' << '\n'
;
t1.test(t1p); // recurse
}
template
<
unsigned int a_FN
>
void test
(
const test1_p<a_FN, 1> & f
)// terminating case: never recurse
{
std::cout
<< '(' << a_F << ':' << c_F << ':' << d_F << ')'
<< "::" << '(' << a_FN << ':' << 1 << ')' << '\n'
;
}
};
template
<
unsigned int a_F,
unsigned int b_F
>
struct test2_p
{
static const unsigned int a = a_F;
static const unsigned int b = b_F;
};
template
<
unsigned int a_F,
unsigned int b_F,
unsigned int c_F
>
struct tester2
{
template
<
unsigned int d_FN
>
void test
(
const test2_p<b_F, d_FN> & f
) // interface guaranteeing b_F of parameter
{ // matches b_F of containing class
// forward to the fucntion that does "work"
// use overload resolution to determine recursion
test(f, is_value_one(value_type<b_F>()));
}
template
<
unsigned int d_FN
>
void test
(
const test2_p<b_F, d_FN> & f,
const false_type * false_f
)// general case: always recurse
{
tester2<a_F, b_F - 1, c_F> t2;
test2_p<b_F - 1, d_FN> t2p;
std::cout
<< '(' << a_F << ':' << b_F << ':' << c_F << ')'
<< "::" << '(' << b_F << ':' << d_FN << ')' << '\n'
;
// use overload resolution to determine recursion
t2.test(t2p, is_value_one(value_type<b_F - 1>())); // recurse
}
template
<
unsigned int d_FN
>
void test
(
const test2_p<b_F, d_FN> & f,
const true_type * true_f
)// terminating case: never recurse
{
std::cout
<< '(' << a_F << ':' << b_F << ':' << c_F << ')'
<< "::" << '(' << b_F << ':' << d_FN << ')' << '\n'
;
}
};
int main()
{
tester1<10, 20, 30> t1;
t1.test(test1_p<10, 5>()); // Success
std::cout << '\n';
t1.test(test1_p<20, 5>()); // Success
std::cout << '\n';
t1.test(test1_p<30, 5>()); // Success
std::cout << '\n';
t1.test(test1_p<1, 5>()); // Success
std::cout << '\n';
tester2<10, 20, 30> t2;
//t2.test(test2_p<10, 5>()); // failure
//std::cout << '\n';
t2.test(test2_p<20, 5>()); // Success
std::cout << '\n';
//t2.test(test2_p<30, 5>()); // failure
//std::cout << '\n';
//t2.test(test2_p<1, 5>()); // failure
//std::cout << '\n';
return(0);
}