I believe, and hope, I got something wrong.
Didn't I warn you about taking the approach you describe below? I think it was over a year ago (but I changed computers so I can't check at the moment).
Updating the special functions library (alongside coding for prospective new members thereof), I hit a problem, which I can't solve in a satisfactory manner. It boils down to the following situation:
<CODE>
#include <complex> #include <iostream> #include <valarray>
// (1) Plain vanilla templated function
template<typename T> T f(T x) { ::std::cout << "Plain vanilla templated function" << ::std::endl;
return(x); }
// (2) Template-template function
template<typename T, template<typename> class U> U<T> f(U<T> x) { ::std::cout << "Template-template function" << ::std::endl;
return(x); }
// (3) Valarray function
template<typename T> ::std::valarray<T> f(const ::std::valarray<T> & x) { ::std::cout << "Valarray function" << ::std::endl;
::std::valarray<T> result = x;
return(result); }
int main() { double x(1);
f(x);
::std::complex<double> y(0,1);
f(y);
const ::std::valarray<double> z(3);
f(z); }
</CODE>
Let us say that f is sinc. In the current library, I have forms (1), for the usual floating point types, and (2), for complex, quaternion and octonions. I wanted to add (3) for valarrays of floating point types (and later, yet another form for valarrays of complex et al), along what exists for, say, sin.
This turns out to be impossible, at least according to my compiler, which complains about an ambiguity in overload resolution between (2) and (3) (if I comment out (2) or (3) it all compiles, with naturally wrong behaviour...). Explicit instantiation (those I have tried, at least...) does not help.
I understand that (1) and (2) are unordered (neither is more specialized than the other). It is also clear that (3) is more specialized than (1). It is less clear to me that (3) should also be more specialized than (2).
Actually, I think that (3) should be more specialized than (2). Maybe your compiler is messed up or the standard needs another DR.
One possible, partial, remedie I have in mind would be to remove (2), add corresponding specializations in the headers for quaternions and octonions, and add specialization for complex and valarray in sinc's header (can't modify <complex> or <valarray> as they are standard). But this is most inelegant, as the code for three specializations is exactly the same, except for types, and this is precisely the reason for using (2)! Furthermore, if we want to add new functions that may work on complex, quaternions and octonions, we would have to modify their headers for each and every one. That's an appaling lack of orthogonality! Another approach would be to leave the headers for quaternions and octonions alone, and stuff sinc's header with lots of specilizations, most of which really are instanciations of (2), and that's both inelegant and a possible maintenance problem.
I welcome any suggestion on that matter (and would most welcome somebody showing me what I got wrong :-) ).
Here's my objections from before:
1. Let's call the complex, quaternion, and octonion types the Cayley family.
2. You specialize certain functions with the signature:
template < typename T, template <typename> class U > f( U<T> x );
with the algorithms that the Cayley-family types need.
3. But you _assumed_ that the set of C++'ed Cayley types and the set of types using the U<T> signature are identical.
4. I say that you have a FUNDAMENTALLY WRONG assumption! You can have: a. Cayley types with a different template signature, or be non-template types b. Non-Cayley types with the given template signature
When I gave the warning, I was being theoretical, and you ignored it. But you finally found a real-life example of the warning (part 4b at least).
I see that Daniel Frey gave a workaround. Don't use it; it's a convoluted solution for something that started fundamentally wrong.
Since partially specializing function templates works strange, you may want to use the "function template calling a class template" idiom.
//=============================================================== namespace detail { template < typename T > T my_sinc_real( T x );
template < typename T > T my_sinc_nonscalar_cayley( T x );
template < typename T > T my_sinc_vector( T x ); }
template < typename T > class sinc_functor { T operator ()( T x ); };
// Add appropriate specializations
template < typename T > T sinc ( T x ) { return sinc_functor<T>()( x ); } //===============================================================
Of course, your main problem is how to specialize the C++ Cayley types we have without repeating a lot of code, but making sure to miss inappropriate types. Maybe we can use some sort of traits system to determine specializations? We'll have to think about this some more....
Daryle
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost