tags:

views:

81

answers:

2

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;
}
+3  A: 

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!)

miked
+4  A: 

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).

Johannes Schaub - litb
Was about to post something similar but I like this version better.
Noah Roberts
`sizeof(char[y-z])+z` and `sizeof(char[z-y+1])+y-1` should also work.
Potatoswatter
The OP should keep in mind though that their example code allows size to be changed. Your code is not based on size like their example attempts but upon what size was originally set to.
Noah Roberts
@Noah ah good point!
Johannes Schaub - litb
@Potatoswatter i was worrying about the size of the array type getting over some implementation limits so i thought i would limit it to maximally `1` :) Tho i suspect the common implementations will support any size there (I'm not totally sure what the Standard's requirements are here).
Johannes Schaub - litb
crazy answer but it works. thanks :)
LoudNPossiblyRight
I would assume that the question had that code as the result of compiling a fast simple example... having `size` non-const would be bad enough, but also keeping it `public` is calling for trouble.
David Rodríguez - dribeas