Reputation: 3893
How do i pull off something that would make the last commented line compile? How would i have to change this code to make it work?
#include<iostream>
using namespace std;
template <int x>
class someclass{
public:
int size;
int intarr[x];
someclass():size(x){}
};
template<int x, int y>
int somefunc(someclass<x> A, someclass<y> B){
return ( A.size > B.size ? A.size : B.size);
}
template<int x, int y, int z>
someclass<x> anotherfunc(someclass<y> A, someclass<z> B){
return ( A.size > B.size ? A : B);
}
int main(){
someclass<5> A;
someclass<10> B;
cout << "SIZE = " << somefunc(A,B) << endl;
//cout << "SIZE = " << (anotherfunc(A,B)).size << endl; //THIS DOES NOT COMPILE
return 0;
}
Upvotes: 1
Views: 218
Reputation: 507393
How is it supposed to know what x
is from outside the function? (It can't look into the body, because in which body of which function it looks depends on what value x
gets!). Also you cannot write that ?:
because both your branches yield totally unrelated types that can't possibly get to a common type. But that operator requires to have a common type for both branches that it evaluates to.
And then the other problem you have that size
is not a compile time constant is already elaborated by another post.
I think this should work:
template<int y, int z>
someclass<sizeof (char[+(y >= z)]) * y> anotherfunc(someclass<y> A, someclass<z> B){
return A;
}
template<int y, int z>
someclass<sizeof (char[+(y < z)]) * z> anotherfunc(someclass<y> A, someclass<z> B){
return B;
}
Now when calling it, y
and z
are deduced by the parameters, and then the parameters are substituted into the return type. The sizeof(char[1 or 0])
will test whether y
or z
is greater. In the respective template that evaluates this to sizeof(char[1])
(which yields 1) we will multiply by the value that is greater (or equal). If the respective template yields sizeof(char[0])
this is a deduction failure (an array can't be of zero size!) and the template won't get selected by overload resolution (known as SFINAE).
The unary +
yields 1
or 0
as int instead of true
or false
, which could cause compiler warnings for some compilers.
There is also the "clean" way with enable_if
. It's not doing hideous sizeof
tricks, but rather using the general established enable_if pattern. Boost has an implementation of it
template<int y, int z>
typename boost::enable_if_c<(y >= z), someclass<y> >::type
anotherfunc(someclass<y> A, someclass<z> B){
return A;
}
template<int y, int z>
typename boost::enable_if_c<(y < z), someclass<z> >::type
anotherfunc(someclass<y> A, someclass<z> B){
return B;
}
This may at first sight look more noisy, but should be easier to understand and faster to follow to folks that are already used to enable_if
(the _c
variant of boost's implementation accepts plain booleans, as above).
Upvotes: 4
Reputation: 3598
Remember, in C++, everything is determined at compile time. In anotherfunc
, you don't know until runtime whether you're returning the "y-typed" or the "z-typed" class. You need to know what you're returning if you're going to return a template class from a function.
There are some tricks you can pull with overloading the cast operator of an intermediate class to get something that looks like it overloads on return type, but that's really abusing the language (and you still need to know the return type at compile time!)
Upvotes: 3