#42177 - sajiimori - Sat May 07, 2005 4:18 am
This code compiles with GCC 3.2.3:
Code: |
template<typename A, typename B>
void f(A, B)
{
}
template<typename A>
void f(A, int)
{
}
|
But this doesn't:
Code: |
template<typename A, typename B>
struct S
{
void method(A, B)
{
}
};
template<typename A>
void S<A, int>::method(A, int)
{
}
|
Though it lets me specialize the method if I provide all the template arguments:
Code: |
void S<int, int>::method(int, int)
{
}
|
Anybody know what's going on?
#42178 - josath - Sat May 07, 2005 4:54 am
you can put methods in structs? i thought you could only put methods in classes.
#42181 - sajiimori - Sat May 07, 2005 5:25 am
Structs and classes are equivalent except structs default to public and classes default to private.
#42185 - poslundc - Sat May 07, 2005 8:14 am
Seems like it would be kosher to me. Could the difference be that the second template also attempts to instantiate an incompletely defined class, whereas the first example only instantiates the function?
Dan.
#42201 - sajiimori - Sat May 07, 2005 6:08 pm
Looks like I can specialize some of the methods on all of the arguments, or all of the methods on some of the arguments (see below), but not some of the methods on some of the arguments.
Code: |
template<typename A>
struct S<A, int>
{
void method(A, int)
{
}
};
|
If I want to specialize a single method on some of the template arguments, is there a better way then duplicating the entire class?
#42214 - MumblyJoe - Sun May 08, 2005 1:49 am
I also just tested this, you pretty much have to specialise the entire class and not just the function it looks like.
Luckily, that gave me an idea, use the funky powers of C++ to solve the problem!
Code: |
template<typename AA, typename BB>
struct method_class
{
void operator()(AA,BB);
};
template<typename A, typename B>
struct S
{
method_class<A,B> method;
};
template<typename A>
struct method_class<A,int>
{
void operator()(A,int){}
}; |
Now if you create a S<anything,int> it creates a method_class<anything,int> inside of itself and uses the partial specialization. The operator() allows it to look enough like a normal member function, with the only obvious problem bieng that it cant DIRECTLY access the member data of the S object calling it, but that can be solved by passing it the "this" pointer as a constructor argument.
It's a bit of a workaround I know, but if this is what you need it works like a charm.
_________________
www.hungrydeveloper.com
Version 2.0 now up - guaranteed at least 100% more pleasing!
#42217 - poslundc - Sun May 08, 2005 2:30 am
Jesus.
Not that I'm unimpressed with your solution, MumblyJoe - I certainly wouldn't have thought of doing it that way - but talk about having to obfuscate your code in order to do what you want to do... I certainly wouldn't want to be the programmer reviewing/maintaining that code.
If you ask me, it's just a poor "feature" of GCC that it doesn't let you create a partial-specialization of a template class unless the first one is completely specified in the second, but if you think about the way template expansion actually works (in theory, anyway), you can chalk it to the same kind of way pre-processor commands like #define and #include are (much more commonly) misused: it is designed as more of a physical tool than a logical one... it does something specific rather than solve a specific problem.
From a practical standpoint, sajimori, while I think your intent is clear the way you were originally writing the code, I think the extra code the compiler wants you to write is preferable to any workaround. This is kind of like defining operators in a class... it's intended to save work down the line, not at the creation stage.
Dan.
#42229 - sajiimori - Sun May 08, 2005 3:28 am
CodeWarrior doesn't let me do it, either. It might not be part of the C++ standard.
MumblyJoe, I was thinking something along those lines, but I ended up duplicating the entire set of classes because the length is comparable with each approach.
Quote: |
talk about having to obfuscate your code in order to do what you want to do |
Isn't that the C++ slogan? For reference, open up Caller.h in our project. Then close it before your brain essplode. Life without closures is fun!
Incidentally, this is the functionality I was implementing when I ran into the problem:
Code: |
// Delayed method calling with up to 3 arguments, some or all of which
// can be provided when creating the caller.
class Test
{
public:
Test(int x)
: m_X(x) {}
double method(int a, short b, float c)
{
return (a + b + c) * m_X;
}
private:
int m_X;
};
void test()
{
Test t(2);
// Create a Caller2 which means two arguments will be provided
// to the call() method.
//
// The template arguments specify that call() will take a short
// and a float, then return a double.
//
// Since the method takes 3 arguments, the first one is provided
// here and stored in the Caller.
//
Caller2<short, float, double>* c = newCaller(&t, &Test::method, 10);
// Calls t.method(10, 20, 30.0f), returning 120.0.
double result = c->call(20, 30.0f);
// Don't forget to clean up.
delete c;
}
|
#42230 - MumblyJoe - Sun May 08, 2005 3:54 am
sajiimori: The original code you posted didn't work in Visual C++, so we can assume if msvc, gcc and codewarrior don't accept it that it's either not allowed by the standard, or not worth doing if three good compilers don't accept it.
poslundc: Yeah, I can't deny that I wouldn't want to maintain that code either, but it occured to me and I thought I would throw it into the ring.
Also, I just tracked down my copy of Thinking in C++ Volume 2 by Bruce Eckel because it has a section on partial template specialisation. All of the examples rewrite the entire classes, not just one of the functions. Bruce Eckel blows me away constantly with the tricks he performs in his books, and if he didn't mention it then it probably can't be done.
I am trying to think of anything I have seen to allow it, but I can't think of any tricks right now besides the one I posted earlier.
_________________
www.hungrydeveloper.com
Version 2.0 now up - guaranteed at least 100% more pleasing!
#42231 - MumblyJoe - Sun May 08, 2005 4:31 am
OK, I know I shouldn't keep thinking about this problem, but not bieng able to do something really elegantly in C++ drives me nuts.
So here we have a far more elegant and maintainable version of my solution:
Code: |
template<typename A, typename B>
struct S
{
void method(A a,B b){method_func(a,b);}
private:
template<typename AA, typename BB>
void method_func(AA,BB){std::cout << "Not specialised!" << std::endl;}
template<typename AA>
void method_func(AA,int){std::cout << "Specialised for 2nd arg as int!" << std::endl;}
};
int _tmain(int argc, _TCHAR* argv[])
{
S<float,float> ff;
S<float,int> fi;
ff.method(1.0f,1.0f);
fi.method(1.0f,1);
return 0;
} |
Note that technically what we are doing here is not a specialisation, but more just overloading method_func in a private sense so that it's arguments are only ever deduced from other member functions, which will call the correct one because they take the template types as arguments. Give it a shot, even making ff in main have an int like 2 as it's 2nd argument still wont make it call the wrong method_func because it is transformed while passing due to the template.
_________________
www.hungrydeveloper.com
Version 2.0 now up - guaranteed at least 100% more pleasing!
#42236 - sajiimori - Sun May 08, 2005 6:23 am
Nice! Unfortunately, I must now throw you a curve. The template argument that I need to specialize is actually the return type of the method. The specialization is for void.
Code: |
template<typename A, typename R>
struct S
{
R method(A)
{
return R();
}
};
template<typename A>
void S<A, void>::method(A)
{
}
|
I hope this does not deprive you of your peace of mind!
#42239 - tepples - Sun May 08, 2005 8:11 am
If it matters, functions in C++ can't be overloaded by return type.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#42240 - MumblyJoe - Sun May 08, 2005 8:21 am
Well I can't deny that when you said that it was the return type you wanted, and that you were specializing for void that I was certainly thrown back a little.
Fortunately for you, I kick ass:
Code: |
template<typename A, typename B>
struct S
{
B method(A a){return method_func<A,B>(a);}
private:
template<typename AA, typename BB>
BB method_func(AA){std::cout << "Not specialised!" << std::endl; return BB();}
template<>
void method_func<A,void>(A){std::cout << "Specialised for return as void!" << std::endl;}
};
int _tmain(int argc, _TCHAR* argv[])
{
S<float,float> ff;
S<float,void> fv;
ff.method(1.0f);
fv.method(1.0f);
return 0;
} |
_________________
www.hungrydeveloper.com
Version 2.0 now up - guaranteed at least 100% more pleasing!
#42259 - sajiimori - Sun May 08, 2005 5:55 pm
That did not compile for me. Even if the template magic worked, I wouldn't expect the compiler to allow returning the value of a void method call.
tepples: It's good that C++ doesn't allow normal overloading on return type because that would be ambiguous in many cases. It works with class template arguments because they are explicit.
#42288 - MumblyJoe - Mon May 09, 2005 1:31 am
Sorry sajiimori, I was working with VC++8 and should have tested it with GCC too.
Luckily, there is a workaround that works with both VC++8 and GCC 3.2.3 (in MinGW) and hopefully with other compilers. I believe the syntax is still very maintainable.
Code: |
#include <iostream>
template<typename A, typename B>
struct S
{
B method(A a){return method_func(a,(B*)(0));}
private:
template<typename AA, typename BB>
BB method_func(AA,BB*){std::cout << "Not specialised!" << std::endl; return BB();}
void method_func(A,void*){std::cout << "Specialised for return as void!" << std::endl;}
};
int main(int argc, char* argv[])
{
S<float,float> ff;
S<float,void> fv;
ff.method(1.0f);
fv.method(1.0f);
return 0;
}
|
By the way, returning the value of a void method call when the calling function also returns void makes neither compiler I tried even give a warning.
_________________
www.hungrydeveloper.com
Version 2.0 now up - guaranteed at least 100% more pleasing!
#42290 - sajiimori - Mon May 09, 2005 1:58 am
Amazing, well done! You've taught me something about C++. ^_^
#42293 - MumblyJoe - Mon May 09, 2005 3:06 am
No problem, now be quiet and don't pick on it or I will be up all night changing it to keep my dignity :P
_________________
www.hungrydeveloper.com
Version 2.0 now up - guaranteed at least 100% more pleasing!