views:

276

answers:

4
template <typename X, typename Y> class A {
    // Use Y::Q, a useful property, not used for specialization.
};
enum Property {P1,P2};
template <Property P> class B {};
class C {};

Is there any way to define a partial specialization of A such that A<C, B<P1> > would be A's normal template, but A<C, B<P2> > would be the specialization?

Edit in response to Marcelo: More specifically, the specialization should be chosen not just with B, but with any type that exhibits a certain property, for example that it's a template whose first argument is P2.

The goal is to use Y to present a nice interface for A, allowing to write something like A<C, Y<P2,Q> >.


Replacing the Y template parameter by a template template parameter would be nice, but is there a way to partially specialize it based on P then?

The intention would be to write something like:

template <typename X, template <Property P> typename Y> class A {};
template <typename X> class A<X,template<> Y<P2> > {}; // <-- not valid

Edit in response to In silico: I said it would be nice to make Y a template template parameter, but actually that defeats the purpose of what I wanted to do, which is to use Y to group logically linked properties together, but still specialize A based on one of those sub-properties.


Is there a way by adding traits to a specialization template <> class B<P2> and then using SFINAE in A? The intention would be to write something like:

template <> class B<P2> {
    typedef int IAmP2;
};

// The following is not valid because it's a simple redefinition.
template <typename X, typename Y> class A {
    // Substitution using this template would fail for Y<P1>, and only the 
    // general template would be left for selection.
    typename Y::IAmP2 skipIfNotP2;
};
A: 

Without template template parameters (which I wouldn't know to use in this context), it should be fairly simple:

template <> class A<C, B<P2> > { ... };

Too simple, in fact. I must be missing something, but I can't see what.

Marcelo Cantos
Thanks for our answer Marcelo.The intention is to have this template specialization apply not just to B<P2>, but to any type Y<P2>.Actually, wanting P2 to be the discriminating property of the types that satisfy this specialization is just *a* way of making them different. I'm flexible on that part.The point is to figure if there's a way to have a partial template specialization matched at instantiation on more general meta-properties/traits of the types than just their names.
Kenzo
A: 

Is this what you want? (Tested with Visual Studio 2005)

enum Property { P1, P2 }; 

template <Property P> class B {}; 
class C {};

// Other similar types, for the purpose of testing
template <Property P> class AnotherB {};
class AnotherC {};

// Primary template
template <typename X, template<Property P> class Y, Property P> class A
{
public:
    A() { ::printf("Primary template\n"); }
};

// Partial specialization for P2
template <typename X, template<Property P> class Y> class A<X, Y, P2>
{
public:
    A() { ::printf("Partially specialized template\n"); }
};

int main()
{
    // Trying out some combinations
    A<C, B, P1> q;               // prints "Primary template"
    A<C, B, P2> w;               // prints "Partially specialized template"
    A<AnotherC, B, P1> e;        // prints "Primary template"
    A<AnotherC, B, P2> r;        // prints "Partially specialized template"
    A<C, AnotherB, P1> t;        // prints "Primary template"
    A<C, AnotherB, P2> y;        // prints "Partially specialized template"
    A<AnotherC, AnotherB, P1> u; // prints "Primary template"
    A<AnotherC, AnotherB, P2> i; // prints "Partially specialized template"
}

Your attempt at partial specialization causes compiler errors because you can pass only templates to template template parameters. You can't pass in template <> class B<P2> for a template template parameter because it's a complete type, not a template.

For the first two lines of code in the main() function, C is a complete type we're passing in to A's type parameter X. B is the template we're passing in to A's template parameter Y, and that template has to accept a Property as the only template parameter. We pass in a Property value (either P1 or P2) to A's nontype parameter P separately. When we pass in P2 for the last argument of the template A, the compiler will see the specialization and use that - otherwise, the compiler will use the primary template A. A similar pattern follows for the next 6 lines.

In silico
Thanks for your answer In silico, it's insightful but still not what I was looking for (my fault). I said in the question that it'd be nice to make Y a template template parameter, but it's actually not. I edited the question to make it clearer.
Kenzo
I might actually use this template template pattern to achieve what I want, but it would require to change the typical call from A<C, B<P2,Q> > to A<C,B<P2,Q>,P2>, which 1) defeats the purpose of a nice interface 2) calls for a mechanism checking that the first parameter of B is the same as the third parameter of A. Thanks again, cheers.
Kenzo
Again, that doesn't work because `B<P2, Q>` is a complete type, which you cannot pass to a template template parameter. Just pass in `B` to the template template parameter of `A` and supply `P2` and `Q` separately. So you would have something like `A<C, B, P2, Q>`. That way, you don't duplicate `P2`, solving problems (1) and (2) simultaneously. Unless I really don't know what you want. :-)
In silico
Indeed I again said that the template template solution but then gave an example that didn't use template templates (A<C, B<P2,Q> > would be an instantiation of template <typename X,typename Y,typename YProperty1> class A {}; , which doesn't use template templates). Sorry, answered to your comment too hastily. I added an example situation in the main question's comments. Thanks for your time.
Kenzo
A: 

I'm going to provide another answer in response to your comment with the Matrix example.

For your Matrix example, you can do this:

enum MatrixOrder { ColumnMajor, RowMajor };

template<MatrixOrder Order> class Dense {};
template<MatrixOrder Order> class Sparse {};

template<typename T, template<MatrixOrder> class Storage, MatrixOrder Order>
class Matrix
{
public:
    Matrix() { ::printf("Primary\n"); }
};

template<typename T, MatrixOrder Order>
class Matrix<T, Dense, Order>
{
public:
    Matrix() { ::printf("Specialized\n"); }
};

int main()
{
    // Trying out some combinations...
    Matrix<double, Dense, ColumnMajor> a;  // Prints "Specialized"
    Matrix<double, Dense, RowMajor> b;     // Prints "Specialized"
    Matrix<double, Sparse, ColumnMajor> c; // Prints "Primary"
    Matrix<double, Sparse, RowMajor> d;    // Prints "Primary"
    Matrix<float, Dense, ColumnMajor> e;   // Prints "Specialized"
    Matrix<float, Dense, RowMajor> f;      // Prints "Specialized"
    Matrix<float, Sparse, ColumnMajor> g;  // Prints "Primary"
    Matrix<float, Sparse, RowMajor> h;     // Prints "Primary"
    return 0;
};

It takes on a similar pattern to my last answer. Now, all storage schemes taking on Dense will be specialized. Hope this helps, at least a little bit. :-)

In silico
It works pretty well, but is kind of a fallback solution if the nicer interface is not possible, because it flattens all parameters to the same level, without logical grouping. Wanted interface can do: Matrix<double,Storage<Sparse,Homogeneous,RowMajor>,640,480>. With the proposed interface, 1) I have to do Matrix<double,Storage<Sparse,Homogeneous,RowMajor>,RowMajor,640,480> which is even longer 2) I can also do Matrix<double,Storage<Sparse,Homogeneous,ColumnMajor>,RowMajor,640,480> which has a semantic mismatch to resolve on the linear indexing.
Kenzo
And those problems are amplified with more parameters. The reason I was wondering if it was possible to do this grouping of template parameters is stuff like:Matrix<double,Storage<Sparse,Homogeneous,RowMajor>,Optimization<Vectorization,Unrolling,Lazy> >. The default way of course is just to flatten all of that to Matrix<double,Sparse,Homogeneous,RowMajor,Vectorization,Unrolling,Lazy> but it's not very nice in the sense it mixes everything and I can't pre-configure storage schemes independently that can be just passed as template arguments. Kind of an extreme example I guess.
Kenzo
Ah, I think I see where this is going. It appears that you want some sort of a policy pattern (see http://en.wikipedia.org/wiki/Policy-based_design). `Storage` and `Optimization` can be the policy classes that dictate the behavior of `Matrix` by providing "pieces" of functionality to `Matrix`.
In silico
Hadn't heard of it. This reminds me of mixins a lot but with possible static resolution and no overhead which sounds kind of too good to be true. Looks like really good stuff. It might just be what I've been looking for, thanks a lot you're awesome.
Kenzo
No worries; glad to be able to point you in a right direction. :-)
In silico
+2  A: 

I've no idea what you mean. Template template parameters seem the solution, although you somehow say they won't work. Why not do this?

template <typename X, typename Y> 
class A {
};

template <typename X, template<typename> class Y, typename P> 
class A< X, Y<P> > {
  /* property is P */
};

For your SFINAE question, yes that is possible too

template <typename X, typename Y, typename Sfinae = void> 
class A {
};

template <typename X, typename Y> 
class A< X, Y, typename Y::IAmP2 > {
  /* Y is the class having a property */
};

class Sample {
  typedef void IAmP2;
};

Still i'm not sure what you mean at all.

Johannes Schaub - litb
Thanks a lot Johannes. Both of your proposals (template template and SFINAE) are spot on. It's exactly the mechanism I was looking for except I couldn't figure the syntax. For the template template solution, expanding the template parameter list is what I oversaw (had read about it once but didn't remember). In the end however I will use the SFINAE solution, which is more flexible in that it doesn't require me to declare a fixed number of template template parameters. Again thanks a bunch, learned some today :).
Kenzo