Compound pointer traits class with method generalizing make_shared and make_unique? - c++

For std::unique_ptr<T>, we have std::make_unique<T[]>();
For std::shared_ptr<T>, there's std::make_shared<T[]>().
A known aphorism in computer science is that we have either none of something, one of something, or arbitrarily many. Well, in this case we're talking about compound pointers, or pointer-like objects. And there are definitely others beyond these two (Example: smart pointers for CUDA-related memory).
Well, I would expect to have something like the following function:
template <typename Ptr>
inline Ptr make(size_t count);
template <typename T, class Deleter = std::default_delete<T>>
inline std::unique_ptr<T[], Deleter> make<std::unique_ptr<T, Deleter>>(size_t count)
{
return std::make_unique<T[]>(count);
}
template <typename T>
inline std::shared_ptr<T[]> make<std::shared_ptr<T[]>>(size_t count)
{
return std::make_shared<T[]>(count);
}
but within a traits class, e.g.
template <typename Ptr>
struct pointer_traits;
as pointer_traits<std::shared_ptr<T>>::make(). And this traits class would also have an element type, a raw pointer type (if one exists), etc. - similarly to the iterator traits class template, std::iterator_traits. It would also exist for the plain pointers, whose maker would be
template <typename T>
inline T* make<T*>(size_t count)
{
return new T[](count);
}
Can I find such this kind of traits class, or the equivalent thereof, in the standard library? I tried and failed... If it doesn't exist, has something similar been proposed/discussed by the standard committee?

Based on #NathanOlliver's insight and suggestion:
The standard library has a std::pointer_traits class, but that one does not have functions which actually perform allocation. It may be useful to propose this to the C++ standards committee, and I'm going to at least try to pursue it soon. Will post an update here if anything comes of it.

Related

C++: Defining a struct that holds containers defined by templates to types defined by the struct [duplicate]

It seems understanding template template parameters will kill me :(, Let me explain what misconception I made in my mind which confuses me:
template<class T>
class B {}; // A templated class
Here is other code:
template<template<class X> class Z = B> // The problem is in this line for me
class BB{};
Note the line in the parameter list of templated class BB, which is:
template<class X> class Z = B
Now, what stops C++ to think that Z is not another templated class Z?
I.e.,
template<class X> class Z {
}
rather than thinking class Z is a templated parameter itself.
Mankarse has answered your question, but I thought I'd chime in anyway.
Template template parameters are just like normal template type parameters, except that they match templates instead of concrete types:
// Simple template class
template <typename Type>
class Foo
{
Type m_member;
};
// Template template class
template <template <typename Type> class TemplateType>
class Bar
{
TemplateType<int> m_ints;
};
If it helps, you can kind of think of them as like function pointers. Normal functions just accept arguments like normal templates just accept types. However, some functions accept function pointers which accept arguments, just like template template types accept templates that accept types:
void foo(int x)
{
cout << x << endl;
}
void bar(void (*f)(int))
{
f(1);
f(2);
}
To answer your question in the comments: template template template parameters are not possible. However, the reason they are not possible is just because the standardisation committee decided that template templates were enough, probably to make lives easier for the compiler implementors. That being said, there's nothing stopping the committee from deciding that they are possible, then things like this would be valid C++:
template <template <template <typename> class> class TemplateTemplateType>
class Baz
{
TemplateTemplateType<Foo> m_foos;
};
typedef Baz<Bar> Example;
// Example would then have Bar<Foo> m_foos;
// which would have Foo<int> m_ints;
Again, you can see parallels in function pointers.
types <=> values
templates <=> functions of values
template templates <=> functions of functions of values
template template templates <=> functions of functions of functions of values
The analogous function to Baz would be:
void baz(void (*g)(void (*f)(int)))
{
g(foo);
}
Where would you use a template template template?
It's pretty far-fetched but I can think of one example: a really generic graph searching library.
Two common algorithms in graph searching are the depth-first search (DFS) and the breadth-first search (BFS). The implementation of the two algorithms is identical except in one regard: DFS uses a stack of nodes whereas BFS uses a queue. Ideally, we'd just write the algorithm once, with the stack/queue as an argument. Also, we'd want to specify the implementation container of the stack or queue, so that we could do something like:
search<Stack, Vector>( myGraph ); // DFS
search<Queue, Deque>( myGraph ); // BFS
But what is a Stack or a Queue? Well, just like in the STL a stack or a queue can be implemented with any kind of container: vectors, deques, lists etc. and could also be stacks of any element type, so our stacks or queues would have the interface:
Stack<Vector, int> // stack of ints, using a vector implementation
Queue<Deque, bool> // queue of bools, using a deque implementation
But Vector and Deque themselves are template types!
So finally, our Stack would be a template template like:
template <template <typename> class Storage, typename Element>
struct Stack
{
void push(const Element& e) { m_storage.push_back(e); }
void pop() { m_storage.pop_back(); }
Storage<Element> m_storage;
};
And our search algorithm would then have to be a template template template!
template <template <template <typename> class, typename> class DataStructure,
template <typename> class Storage,
typename Graph>
void search(const Graph& g)
{
DataStructure<Storage, typename Graph::Node> data;
// do algorithm
}
That would be pretty intense, but hopefully you get the idea.
Remember: template template templates are not legal C++, so this whole graph search thing won't actually compile. It's just a "what if?" :)
This is part of the syntax of the language (which is monstrous and massively context-dependent). If template<class X> class Z occurs in a template-parameter-list then it is interpreted as declaration of a formal parameter Z with the kind (like a meta-type; kinds classify types in the same way types classify values) "template class taking one class argument".
The usage examples in the accepted answer are misleading,
especially for beginners. Granted it's hard to come up with anything that won't be contrived, but we should at least contrive something that doesn't contradict the overall principles. Template parameters should be used only when the user of our interface can't specify the type of the template for one or the other reason, and we need to do it for them. In the Stack example we ask for both Storage and Element, only to instantiate Storage with that very Element, which is entirely unnecessary, the user can easily perform a basic substitution:
Stack<deque<int>> my_stack;
And all the stack needs to do is this:
template <typename Storage>
struct Stack
{
void push(typename Storage::const_reference e) { m_storage.push_back(e); }
void pop() { m_storage.pop_back(); }
Storage m_storage;
typename Storage::reference top() { return m_storage.back(); }
};
It doesn't in any way decide for the user what the element type is, so it does not need the template parameter. Hence the search becomes
template <template <typename> class DataStructure,
template <typename> class Storage,
typename Graph>
void search(const Graph& g, typename Graph::const_reference)
{
DataStructure<Storage<typename Graph::Node>> data;
// do algorithm
}
Here I guess we assume that internal Graph::Node type is not accessible to the user, and search is somehow a friend function of the Graph, which seems to make some sense. However, do we actually need to fill the structure with graph nodes, or simply references to them? Can the user not refer to the nodes in any way? If not, why is it called a graph, and not, say, slow_unordered_set? So lets imagine for a second they have an access to some node reference/pointer type, then they can do this:
search<Stack<vector<Graph::node_ptr>>>(graph, 10);
The function simplifies further to this:
template <typename StackStructure, typename Graph>
void search(const Graph& g, typename Graph::const_reference)
{
StackStructure data;
// do algorithm
}
Gosh darn it, now it's more generic than ever! Do you want to specify an allocator for the storage? No problem, just do it. You instead wanted some statically allocated vector that requires maximum size parameter? Go right ahead. Want to implement the stack from scratch altogether? Well, as long as it quacks like a stack...
Perhaps a more appropriate example
of a template with template parameters would be some class that represents a complex system and uses some Storage template for a bunch of internal structures, and for some reason is parameterized on that Storage template:
template <template <typename> class Storage>
class System
{
Storage<Component_1> components_1;
Storage<Component_2> components_2;
Storage<Component_3> components_3;
Storage<MetaInfo> registry;
public:
// some inane interface
};
If you ask me - this code reeks, but it's not like I wouldn't write it.
Now that we have this semi-appropriate example for a template with a template parameter, we can contrive something for a template with a template parameter that itself has a template parameter: Imagine somehow we end up with like 10 of these System classes that all have the same interface, all parameterized on a Storage template, but otherwise very VERY different. Brace yourselves for the SuperSystem, an even more complicated class, that uses a bunch of our systems, but CRUCIALLY needs to decide itself what Storage templates to use with each system.
template< template< template <typename> class Storage> class System>
class SuperSystem
{
System<Vector> system_1;
System<OtherVector> system_2;
System<List> system_3;
public:
// absolutely bonkers interface
};
We want to specify something down the template hierarchy we're dealing with here, but still leave something up the hierarchy customizable. For some reason we don't know what exact system we will be dealing with, but we know something very specific about all of them, that we absolutely need to go our way. This is an overarching theme with these examples, our goal is not to make things more generic and customizable, but the opposite - we want to lock down certain deeply embedded things.
TL;DR
In my experience you would only encounter good use cases for templates with template parameters when knee deep in a meta programming library. A rule of thumb: if you can recognize this pattern
template<...> struct f { typedef ... type; };
as a type function, then in that mindset you are allowed to use templates with template parameters, and maybe ponder about anything deeper. Otherwise slap yourself on the wrist.

What is the difference between a trait and a policy?

I have a class whose behavior I am trying to configure.
template<int ModeT, bool IsAsync, bool IsReentrant> ServerTraits;
Then later on I have my server object itself:
template<typename TraitsT>
class Server {…};
My question is for my usage above is my naming misnamed? Is my templated parameter actually a policy instead of a trait?
When is a templated argument a trait versus a policy?
Policies
Policies are classes (or class templates) to inject behavior into a parent class, typically through inheritance. Through decomposing a parent interface into orthogonal (independent) dimensions, policy classes form the building blocks of more complex interfaces. An often seen pattern is to supply policies as user-definable template (or template-template) parameters with a library-supplied default. An example from the Standard Library are the Allocators, which are policy template parameters of all STL containers
template<class T, class Allocator = std::allocator<T>> class vector;
Here, the Allocator template parameter (which itself is also a class template!) injects the memory allocation and deallocation policy into the parent class std::vector. If the user does not supply an allocator, the default std::allocator<T> is used.
As is typical in template-based polymporphism, the interface requirements on policy classes are implicit and semantic (based on valid expressions) rather than explicit and syntactic (based on the definition of virtual member functions).
Note that the more recent unordered associative containers, have more than one policy. In addition to the usual Allocator template parameter, they also take a Hash policy that defaults to std::hash<Key> function object. This allows users of unordered containers to configure them along multiple orthogonal dimensions (memory allocation and hashing).
Traits
Traits are class templates to extract properties from a generic type. There are two kind of traits: single-valued traits and multiple-valued traits. Examples of single-valued traits are the ones from the header <type_traits>
template< class T >
struct is_integral
{
static const bool value /* = true if T is integral, false otherwise */;
typedef std::integral_constant<bool, value> type;
};
Single-valued traits are often used in template-metaprogramming and SFINAE tricks to overload a function template based on a type condition.
Examples of multi-valued traits are the iterator_traits and allocator_traits from the headers <iterator> and <memory>, respectively. Since traits are class templates, they can be specialized. Below an example of the specialization of iterator_traits for T*
template<T>
struct iterator_traits<T*>
{
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
using iterator_category = std::random_access_iterator_tag;
};
The expression std::iterator_traits<T>::value_type makes it possible to make generic code for full-fledged iterator classes usable even for raw pointers (since raw pointers don't have a member value_type).
Interaction between policies and traits
When writing your own generic libraries, it is important to think about ways users can specialize your own class templates. One has to be careful, however, not to let users fall victim to the One Definition Rule by using specializations of traits to inject rather than to extract behavior. To paraphrase this old post by Andrei Alexandrescu
The fundamental problem is that code that doesn't see the specialized
version of a trait will still compile, is likely to link, and
sometimes might even run. This is because in the absence of the
explicit specialization, the non-specialized template kicks in, likely
implementing a generic behavior that works for your special case as
well. Consequently, if not all the code in an application sees the
same definition of a trait, the ODR is violated.
The C++11 std::allocator_traits avoids these pitfalls by enforcing that all STL containers can only extract properties from their Allocator policies through std::allocator_traits<Allocator>. If users choose not to or forget to supply some of the required policy members, the traits class can step in and supply default values for those missing members. Because allocator_traits itself cannot be specialized, users always have to pass a fully defined allocator policy in order to customize their containers memory allocation, and no silent ODR violations can occur.
Note that as a library-writer, one can still specialize traits class templates (as the STL does in iterator_traits<T*>), but it is good practice to pass all user-defined specializations through policy classes into multi-valued traits that can extract the specialized behavior (as the STL does in allocator_traits<A>).
UPDATE: The ODR problems of user-defined specializations of traits classes happen mainly when traits are used as global class templates and you cannot guarantee that all future users will see all other user-defined specializations. Policies are local template parameters and contain all the relevant definitions, allowing them to be user-defined without interference in other code. Local template parameters that only contain type and constants -but no behaviorally functions- might still be called "traits" but they would not be visible to other code like the std::iterator_traits and std::allocator_traits.
I think you will find the best possible answer to your question in this book by Andrei Alexandrescu. Here, I will try to give just a short overview. Hopefully it will help.
A traits class is class that is usually intended to be a meta-function associating types to other types or to constant values to provide a characterization of those types. In other words, it is a way to model properties of types. The mechanism normally exploits templates and template specialization to define the association:
template<typename T>
struct my_trait
{
typedef T& reference_type;
static const bool isReference = false;
// ... (possibly more properties here)
};
template<>
struct my_trait<T&>
{
typedef T& reference_type;
static const bool isReference = true;
// ... (possibly more properties here)
};
The trait metafunction my_trait<> above associates the reference type T& and the constant Boolean value false to all types T which are not themselves references; on the other hand, it associates the reference type T& and the constant Boolean value true to all types T that are references.
So for instance:
int -> reference_type = int&
isReference = false
int& -> reference_type = int&
isReference = true
In code, we could assert the above as follows (all the four lines below will compile, meaning that the condition expressed in the first argument to static_assert() is satisfied):
static_assert(!(my_trait<int>::isReference), "Error!");
static_assert( my_trait<int&>::isReference, "Error!");
static_assert(
std::is_same<typename my_trait<int>::reference_type, int&>::value,
"Error!"
);
static_assert(
std::is_same<typename my_trait<int&>::reference_type, int&>::value,
"Err!"
);
Here you could see I made use of the standard std::is_same<> template, which is itself a meta-function that accepts two, rather than one, type argument. Things can get arbitrarily complicated here.
Although std::is_same<> is part of the type_traits header, some consider a class template to be a type traits class only if it acts as a meta-predicate (thus, accepting one template parameter). To the best of my knowledge, however, the terminology is not clearly defined.
For an example of usage of a traits class in the C++ Standard Library, have a look at how the Input/Output Library and the String Library are designed.
A policy is something slightly different (actually, pretty different). It is normally meant to be a class that specifies what the behavior of another, generic class should be regarding certain operations that could be potentially realized in several different ways (and whose implementation is, therefore, left up to the policy class).
For instance, a generic smart pointer class could be designed as a template class that accepts a policy as a template parameter for deciding how to handle ref-counting - this is just a hypothetical, overly simplistic, and illustrative example, so please try to abstract from this concrete code and focus on the mechanism.
That would allow the designer of the smart pointer to make no hard-coded commitment as to whether or not modifications of the reference counter shall be done in a thread-safe manner:
template<typename T, typename P>
class smart_ptr : protected P
{
public:
// ...
smart_ptr(smart_ptr const& sp)
:
p(sp.p),
refcount(sp.refcount)
{
P::add_ref(refcount);
}
// ...
private:
T* p;
int* refcount;
};
In a multi-threaded context, a client could use an instantiation of the smart pointer template with a policy that realizes thread-safe increments and decrements of the reference counter (Windows platformed assumed here):
class mt_refcount_policy
{
protected:
add_ref(int* refcount) { ::InterlockedIncrement(refcount); }
release(int* refcount) { ::InterlockedDecrement(refcount); }
};
template<typename T>
using my_smart_ptr = smart_ptr<T, mt_refcount_policy>;
In a single-threaded environment, on the other hand, a client could instantiate the smart pointer template with a policy class that simply increases and decreases the counter's value:
class st_refcount_policy
{
protected:
add_ref(int* refcount) { (*refcount)++; }
release(int* refcount) { (*refcount)--; }
};
template<typename T>
using my_smart_ptr = smart_ptr<T, st_refcount_policy>;
This way, the library designer has provided a flexible solution that is capable of offering the best compromise between performance and safety ("You don't pay for what you don't use").
If you're using ModeT, IsReentrant, and IsAsync to control the behaviour of the Server, then it's a policy.
Alternatively, if you are want a way to describe the characteristics of the server to another object, then you could define a traits class like so:
template <typename ServerType>
class ServerTraits;
template<>
class ServerTraits<Server>
{
enum { ModeT = SomeNamespace::MODE_NORMAL };
static const bool IsReentrant = true;
static const bool IsAsync = true;
}
Here are a couple of examples to clarify Alex Chamberlain's comment:
A common example of a trait class is std::iterator_traits. Let's say we have some template class C with a member function which takes two iterators, iterates over the values, and accumulates the result in some way. We want the accumulation strategy to be defined as part of the template too, but will use a policy rather than a trait to achieve that.
template <typename Iterator, typename AccumulationPolicy>
class C{
void foo(Iterator begin, Iterator end){
AccumulationPolicy::Accumulator accumulator;
for(Iterator i = begin; i != end; ++i){
std::iterator_traits<Iterator>::value_type value = *i;
accumulator.add(value);
}
}
};
The policy is passed in to our template class, while the trait is derived from the template parameter. So what you have is more akin to a policy. There are situations where traits are more appropriate, and where policies are more appropriate, and often the same effect can be achieved with either method leading to some debate about which is most expressive.
Policies are passed through the API by the user to actively choose which code paths should be followed inside a certain interface.
Traits on the other hand are used by the library author to select certain overloads based on what the user passes into the API.
Traits are a way of the library author reacting to the user input, whereas policies are a way for the user to actively influence the library behaviour.

Partial specialization syntax confusion

To define a specialization that is used for every Vector of pointers and only for
Vectors of pointers, we need a partial specialization:
template <class T> class Vector <T *> : private Vector<void *> {
public:
typedef Vector<void*> Base;
Vector(): Base() {}
explicit Vector(int i) : Base(i ) {}
T *& elem(int i ) { return static_cast <T *&> (Base::elem(i)); }
T *& opeator[](int i) { return static_cast <T *&>(Base::operator[](i )); }
//...
};
This definition has me in a tizzy. This is related to partial specialization but i don't understand the syntax. private Vector<void *> definition part looks like a parent class to me.
Why not specify Vector <void *> in template <class T> class Vector <void *>.
It would be great if anybody can breakdown the definition part. (sorry if its too much to ask)
Forget about the inheritance, which has nothing to do with the problem at hand.
Partial specialization means that you make a new template from an existing one which is more specialized, but still generic, by matching a more restrictive pattern. The general pattern of your example is like this:
template <typename T> class Foo; // primary template
template <typename U> class Foo<U*>; // partial specialization
template <> class Foo<fool>; // full specialization
The first line is the primary template and matches everything that is not matched by a more specialized form. The third line defines an actual type (not a template!) Foo<fool> (for some given type fool). The middle line, on the other hand, is still a template, but it only matches a type of the form T = U *, i.e. a pointer:
Foo<char> x; // uses primary template with T = char
Foo<fool> y; // uses full specialization (nothing to be matched)
Foo<int*> z; // uses partial specialization, matching U = int
About the Vector<void*>: It just turns out that the author chooses to define the partially-specialized Vector<U*> as deriving from a fixed class Vector<void*> (which would have to be fully specialized elsewhere).
Your question is about template specialization, and that's what's going on here.
This is a template class definition of a Vector<T> being specialized for a pointer type T. Presumably, you've defined the Vector<T> template elsewhere, so this code is only specializing it for the circumstance where T is a pointer. Hence the Vector<T*>.
Since it is a specialization, the Vector<T*> derives from the Vector 'base-template', which is a Vector<void *>. The other code, which isn't detailed in your example, would handle specifics of working with a pointer type as the contained data.
Basically, this is making the members of Vector<Ptr>, for any pointer type, forward to Vector<void*> (using a combination of inheritance and explicit base calls plus casting). This prevents the compiler from making many identical versions of the same code, differing only by the pointer type, possibly saving space in the final executable (although most linkers are smart enough to combine identical functions, see for instance "COMDAT folding")
This is problematic according to the Standard because the pointers don't have the same alignment restrictions. Not all object pointers necessarily have the same size in a conforming implementation. In fact, I'm very surprised that the compiler accepts static_cast<T*&>(a_void_ptr) at all. Static casting between void* and T* is allowed of course, but this deals with reference-to-pointer types, which are unrelated. It should require reinterpret_cast.
It seems that the author of the code wants to exploit the fact that the code for different pointer types is identical. It is probably just a premature optimization to reduce code size, or "code bloat". The idea is most likely that each new pointer type will only add a layer of static_cast to the void pointer code.

Is this a situation where I'd want to use "using"?

I'm not familiar with C++0x. I just started learning C++ myself about 6 months ago, I have a fairly strong grasp though (for a beginner).
I have a templated class:
template <typename T>
class Node
{
...
}
Then later, I have this:
template <typename T>
class BinaryTree
{
protected:
typedef Node<T>* node_t;
...
}
Here, the Binary tree class is serving as a "base class" that can be extended by specializations of binary trees. (AVL Tree, Red-Black, etc.,) The node typedef is protected, because the idea is the specializations will be able to use it...and they can, but it looks pretty awful.
For example, in my BiTree class (my creative name for the most generic binary tree, basically a BST), we have this:
template <typename T>
class BiTree : public BinaryTree<T>
{
private:
typedef typename BinaryTree<T>::node_t node_t; // Yuck
...
}
To make matters worse, I'm one of those people who likes to specify functions outside of a class, so when I want to say node_t is the return type...well, have a look...
template <typename T>
typename BiTree<T>::node_t
BiTree<T>::insert(BiTree<T>::node_t& node, T data)
{
...
}
Is there a way to just use node_t? That was sort of the whole point of inheriting the typedef from the base class. Is this what the using keyword in C++0x is for? How would I apply it to this situation? Thanks.
EDIT: The reason I'm wondering if it's useful is because of this question: C++ template typedef
The answer to your question is no, it isn't applicable. using in the context you mean is intended for renaming a templated type while retaining its templated nature. You have a specific instance of the template in mind, so it is not appropriate.
However, part of your concern seems to simply be the overabundance of BiTree<T>:: in your function definition. It doesn't seem that bad to me; you get used to seeing constructs like that. But it can be reduced if you want.
What you started with:
template <typename T>
typename BiTree<T>::node_t BiTree<T>::insert(BiTree<T>::node_t& node, T data)
{ ... }
First of all, once you name the function, you're already "inside" the class BiTree<T>, so the compiler will look inside it for types of your arguments.
template <typename T>
typename BiTree<T>::node_t BiTree<T>::insert(node_t& node, T data)
{ ... }
Another new feature of C++0x is the ability to wait to declare the result of a function until after you declare its arguments. It is intended for use in situations where the type of the result depends on the types of the arguments, but it is useful here as well for the same reason as above. The compiler will consider types within BiTree<T> when analyzing it:
template<typename T>
auto BiTree<T>::insert(node_t& node, T data) -> node_t
{ ... }
Almost no repetition. You can technically go one step further:
template<typename T>
auto BiTree<T>::insert(node_t& node, T data)
-> std::remove_reference<decltype(node)>::type
{ ... }
Now, you don't even repeat the parameter type, but getting the return type correct is notably more difficult [as evidenced by the fact that I got it wrong initially ;-)].
Ehm... base class typedefs are available in the derived class without any hocus pocus, just use node_t (though _t is a bad suffix, as all names ending in it are reserved by the POSIX standard). But I'm wondering, why do you make it protected / private if you want to return such a node from insert ? How should that be used, as nobody outside of the class hierarchy can use the node_t?

Template arguments

I have ClassA<ARG_TYPE> and ClassB<ARG_TYPE>. Now I want want to use ClassC, that has common ARG_TYPE and mentioned classes as template arguments.
ClassC<ARG_TYPE, ClassA<ARG_TYPE>, ClassB<ARG_TYPE>> is easy.
But is it possible do declare ClassC<ARG_TYPE, ClassA, ClassB> so that both A and B classes would know to use ARG_TYPE as their template argument?
Yes, it can be done, through the use of "template template arguments".
Declare ClassC as follows :
template<typename Arg,
template<typename T_Arg> class T_ClassA,
template<typename T_Arg> class T_ClassB>
class ClassC
{
typedef T_ClassA<Arg> MyClassA;
typedef T_ClassB<Arg> MyClassB;
// Use MyClassA and MyClassB
};
Use
ClassC<Arg, ClassA, ClassB>
and it should work fine.
It can be done, however I don't like this solution much.
The issue is that suppose I define:
template <class Arg, class Policy> class Polymorph;
which is a generalization of your MyClassA type but whose behavior can be changed (at compile-time) through the use of policies (think Allocator for standard container for example).
Then I just can't use your interface, because my class takes 2 parameters while your specification only works with one...
Therefore, I much prefer the first approach, even if a bit more verbose. In order to benefit from both you have 2 solutions (both involving duck-typing):
you just define a first class using the first approach and then a second class using the second approach which forwards the (now complete) arguments to the first. This way people with template classes that do not match your requirements will still be able to benefit from your work.
it's up to the user to provide the duck-typing for its class so that it creates a one argument class from a several arguments class. Inheritance and template don't mix well though so this may cause issues and I would favor the previous idea (put the burden on the library writer).
This changes with the advent of C++0x: the later solution becomes a template'd typedef which works much better :)
Here is an example in C++0x:
template <class Arg, template <class> class ClassA>
struct MyTemplate
{
typedef ClassA<Arg> classA_type;
};
template <class Arg, class Policy> class Polymorph;
// C++0x required for following declaration
template <class Arg>
typedef Polymorph<Arg, ConservativePolicy> ConservativePolymorph;
typedef MyTemplate<int, ConservativePolymorph> MyTemplateC;
If you don't have C++0x available, shy away from this and re-use the STL way, even if it's more verbose.