digitalmars.D.learn - template member function confusion
- Francois Chabot (34/34) Apr 08 2012 Hello, I've been getting into the language recently, and ror the
- Jonathan M Davis (16/60) Apr 08 2012 Two issues here.
- Francois Chabot (9/29) Apr 08 2012 I could see this working, but I'll steer clear of this in this
- Jonathan M Davis (15/19) Apr 08 2012 Of course, it's something that people have been asking for. It would be
- Stefan (23/45) Apr 08 2012 If you ask me, choose C - that's the most consistent solution.
Hello, I've been getting into the language recently, and ror the most part, it's going pretty smoothly. I have finally run into the first major snag that's really making me scratch my head. Mind you, I can easily code around it, but I'd like to understand why it's not working: Given (roughly) the following code: class Binding { ... } class Bar { Binding[string] Bindings ; //cached function void foo( Foo target , const ref Matrix44 val ) { ... } void foo( Foo target , const ref Vec4 val ) { ... } //... more function... //convenience interface for non-critical code-paths void foo(T)( string target , const ref T val ) { foo( Bindings[target] , val ) ; } } DMD gives me the following: Error: Bar.foo(T) conflicts with function Bar.foo at ... Now, I can easily A) Change the name of either one the functions (which yields a slightly less elegant interface) B) Not use a template and put string versions of all the foos (which yields ugly code) C) Make the binding-based interface a template and implement the functions through specialization (as they are unfortunately different enough to not be templatable). While maintaining the interface and conciseness, it feels like a hack to me. I just can't wrap my head around why my current implementation cannot work. Any insight?
Apr 08 2012
On Sunday, April 08, 2012 22:51:51 Francois Chabot wrote:Hello, I've been getting into the language recently, and ror the most part, it's going pretty smoothly. I have finally run into the first major snag that's really making me scratch my head. Mind you, I can easily code around it, but I'd like to understand why it's not working: Given (roughly) the following code: class Binding { ... } class Bar { Binding[string] Bindings ; //cached function void foo( Foo target , const ref Matrix44 val ) { ... } void foo( Foo target , const ref Vec4 val ) { ... } //... more function... //convenience interface for non-critical code-paths void foo(T)( string target , const ref T val ) { foo( Bindings[target] , val ) ; } } DMD gives me the following: Error: Bar.foo(T) conflicts with function Bar.foo at ... Now, I can easily A) Change the name of either one the functions (which yields a slightly less elegant interface) B) Not use a template and put string versions of all the foos (which yields ugly code) C) Make the binding-based interface a template and implement the functions through specialization (as they are unfortunately different enough to not be templatable). While maintaining the interface and conciseness, it feels like a hack to me. I just can't wrap my head around why my current implementation cannot work. Any insight?Two issues here. 1. You cannot currently overload a templated function with a non-templated function or vice versa: http://d.puremagic.com/issues/show_bug.cgi?id=1528 This is a bug which should work and should work at some point. The workaround is to templatize the non-templated functions with empty parens. e.g. void foo()(Foo target, const ref Matrix44 val) 2. Template functions are non-virtual and will _never_ be virtual. This probably isn't causing you any compilation issues, but it does mean that you must be careful with using templated functions in classes. It means that templatizing all of the foos will mean that none of them are virtual (though that's arguably better than making some of them virtual and some not, since that has its own set of issues). Regardless, if you have an API which relies on having a templated function be virtual, you're going to have to find a way to work around it, because templated functions _can't_ be virtual. - Jonathan M Davis
Apr 08 2012
Two issues here. 1. You cannot currently overload a templated function with a non-templated function or vice versa: http://d.puremagic.com/issues/show_bug.cgi?id=1528 This is a bug which should work and should work at some point.Thank you very much for the confirmation that I'm not crazy here.The workaround is to templatize the non-templated functions with empty parens. e.g. void foo()(Foo target, const ref Matrix44 val)I could see this working, but I'll steer clear of this in this particular case. "Dirtying" the preferred interface just for the sake of a convenience function is not exactly... nice...2. Template functions are non-virtual and will _never_ be virtual. This probably isn't causing you any compilation issues, but it does mean that you must be careful with using templated functions in classes. It means that templatizing all of the foos will mean that none of them are virtual (though that's arguably better than making some of them virtual and some not, since that has its own set of issues). Regardless, if you have an API which relies on having a templated function be virtual, you're going to have to find a way to work around it, because templated functions _can't_ be virtual.I was already aware of the non-virtualness of templates, and to tell the truth, I much prefer it this way. Maybe it's my C++ background showing here, but is this something people have been asking for? It sounds crazy to me. Once again, thanks for the help!
Apr 08 2012
On Sunday, April 08, 2012 23:59:01 Francois Chabot wrote:I was already aware of the non-virtualness of templates, and to tell the truth, I much prefer it this way. Maybe it's my C++ background showing here, but is this something people have been asking for? It sounds crazy to me.Of course, it's something that people have been asking for. It would be fantastic to be able to have templated functions which are virtual. And if you don't really understand how templates work or don't think it through enough, it seems crazy that they _wouldn't_ be virtual. But there are very practical reasons why doing so is more or less infeasible (it's certainly infeasible with how things currently work in D), and once it's explained how templates don't interact with virtual tables very well and all that, it becomes pretty obvious that there's no way that templates can be virtual. There's no question that there are people who want it though. And the fact that some stuff in D really needs to be templated (e.g. a lot of range-based stuff really only works well if it's templated, and you can't support multiple string types very well without templates) makes it so that the lack of virtual templates in classes can be frustrating at times. - Jonathan M Davis
Apr 08 2012
On Sunday, 8 April 2012 at 20:51:52 UTC, Francois Chabot wrote:class Bar { Binding[string] Bindings ; //cached function void foo( Foo target , const ref Matrix44 val ) { ... } void foo( Foo target , const ref Vec4 val ) { ... } //... more function... //convenience interface for non-critical code-paths void foo(T)( string target , const ref T val ) { foo( Bindings[target] , val ) ; } } DMD gives me the following: Error: Bar.foo(T) conflicts with function Bar.foo at ... Now, I can easily A) Change the name of either one the functions (which yields a slightly less elegant interface) B) Not use a template and put string versions of all the foos (which yields ugly code) C) Make the binding-based interface a template and implement the functions through specialization (as they are unfortunately different enough to not be templatable). While maintaining the interface and conciseness, it feels like a hack to me.If you ask me, choose C - that's the most consistent solution. It's perfectly OK to have specialized templates. That's not a hack, it's a feature ;-) Regarding the virtualization discussion: You could still use method dispatcher pairs, such as // Specialization void foo(T: Matrix44)( Foo target , const ref Matrix44 val ) { myfoo(target , val); } void myfoo( Foo target , const ref Matrix44 val ) { /* real functionality here */ } // general "swiss knife" void foo(T)( string target , const ref T val ) { myfoo(target , val); } void myfoo( Foo target , const ref Variant val ) { /* real functionality here */ } Then the myfoo methods would be overloadable. If you want the "total hack", you could wrap the specialization pairs into mixins. This way, only the foo wrappers are visible to the human user, but the compiler also sees the overloadable, "ordinary" methods. Cheers, Stefan
Apr 08 2012