Every allocator class must have an interface similar to the following:
template<class T>
class allocator
{
...
template<class Other>
struct rebind { typedef allocator<Other> other; };
};
And classes that use allocators do something redundant like this:
template<class T, class Alloc = std::allocator<T> >
class vector { ... };
But why is this necessary?
In other words, couldn't they have just said:
template<class T>
class allocator { ... };
template<class T, template<class> class Alloc = std::allocator>
class vector { ... };
which is both more elegant, less redundant, and (in some similar situations) potentially safer?
Why did they go the rebind route, which also causes more redundancy (i.e. you have to say T twice)?
(Similar question goes to char_traits and the rest... although they don't all have rebind, they could still benefit from template template parameters.)
Edit:
But this won't work if you need more than 1 template parameter!
Actually, it works very well!
template<unsigned int PoolSize>
struct pool
{
template<class T>
struct allocator
{
T pool[PoolSize];
...
};
};
Now if vector was only defined this way:
template<class T, template<class> class Alloc>
class vector { ... };
Then you could just say:
typedef vector<int, pool<1>::allocator> int_vector;
And it would work perfectly well, without needing you to (redundantly) say int twice.
And a rebind operation inside vector would just become Alloc<Other> instead of Alloc::template rebind<Other>::other.
A quoted text from Foundations of Algorithms in C++11, Volume 1, chap 4, p. 35 :
template <typename T>
struct allocator
{
template <typename U>
using rebind = allocator<U>;
};
sample usage :
allocator<int>::rebind<char> x;
In The C++ Programming Language, 4th edition, section 34.4.1, p. 998, commenting the 'classical' rebind member in default allocator class :
template<typename U>
struct rebind { using other = allocator<U>;};
Bjarne Stroustrup writes this:
The curious rebind template is an archaic alias. It should have been:
template<typename U>
using other = allocator<U>;
However, allocator was defined before such aliases were supported by C++.
But why is this necessary?
What if your allocator class has more than one template argument?
That's pretty much it in terms of why it is generally discouraged to use template template arguments, in favor of using normal template arguments, even if it means a bit of redundancy at the instantiation site. In many cases (however, probably not for allocators), that argument might not always be a class template (e.g., a normal class with template member functions).
You might find it convenient (within the implementation of the container class) to use a template template parameter just because it simplifies some of the internal syntax. However, if the user has a multi-argument class template as an allocator he wants to use, but you require the user to provide an allocator which is a single-argument class template, you will in effect force him to create a wrapper for almost any new context in which he must use that allocator. This not only unscalable, it can also become very inconvenient to do. And, at this point, that solution is far from being the "elegant and less redundant" solution you originally thought it would be. Say you had an allocator with two arguments, which of the following is the easiest for the user?
std::vector<T, my_allocator<T,Arg2> > v1;
std::vector<T, my_allocator_wrapper<Arg2>::template type > v2;
You basically force the user to construct a lot of useless things (wrappers, template aliases, etc.) just to satisfy your implementation's demands. Requiring the author of a custom allocator class to supply a nested rebind template (which is just a trivial template alias) is far easier than all the contortions you require with the alternative approach.
In your approach you are forcing the allocator to be a template with a single parameter, which might not be always the case. In many cases, allocators can be non-template, and the nested rebind can return the same type of the allocator. In other cases the allocator can have extra template arguments. This second case is the case of std::allocator<> which as all templates in the standard library is allowed to have extra template arguments as long as the implementation provides default values. Also note that the existence of rebind is optional in some cases, where allocator_traits can be used to obtain the rebound type.
The standard actually mentions that the nested rebind is actually just a templated typedef:
§17.6.3.5/3
Note A: The member class template rebind in the table above is
effectively a typedef template. [ Note: In general, if the name
Allocator is bound to SomeAllocator<T>, then
Allocator::rebind<U>::other is the same type as SomeAllocator<U>,
where someAllocator<T>::value_type is T and SomeAllocator<U>::value_type is U. — end note ] If Allocator is a class template
instantiation of the form SomeAllocator<T, Args>, where Args is zero
or more type arguments, and Allocator does not supply a rebind member
template, the standard allocator_traits template uses SomeAllocator<U, Args> in place of Allocator:: rebind<U>::other by default. For
allocator types that are not template instantiations of the above
form, no default is provided.
Suppose you want to write a function taking all sorts of vectors.
Then it is much more convenient being able to write
template <class T, class A>
void f (std::vector <T, A> vec) {
// ...
}
than having to write
template <class T, template <class> class A>
void f (std::vector <T, A> vec) {
// ...
}
In most of the cases, such a function does not care about the allocator anyway.
Further note that allocators are not required to be a template. You could write separate classes for particular types that need to be allocated.
An even more convenient way of designing allocators would probably have been
struct MyAllocator {
template <class T>
class Core {
// allocator code here
};
};
Then it would have been possible to write
std::vector <int, MyAllocator> vec;
rather than the somewhat misleading expression
std::vector <int, MyAllocator<int> > vec;
I am not sure whether the above MyAllocator is permitted to be used as an allocator after adding a rebind, i.e. whether the following is a valid allocator class:
struct MyAllocator {
template <class T>
class Core {
// allocator code here
};
template <class T>
struct rebind { using other=Core<T>; };
};
Related
I saw some examples of using allocator as a class member in a template class. Such like:
template <class T, class Alloc = std::allocator<T>> class myVector
{
public:
...
protected:
std::allocator<value_type> _alloc;
...
private:
...
};
But the code still works when I delete the default-value template argument like template <class T> class myVector.
So do we need to add a default-value template argument when we have a allocator as class member? If the answer is yes, why?
The code shown is probably wrong: It should use the provided Alloc type to allocate, instead of hard-coding std::allocator. (And also take advantage of the empty-base-class optimization, to avoid increasing the container size if the allocator is an empty type)
Sorry for the vague title, but I don't know exactly which is the correct term (and even if it's possible to do the following).
Let's say I have somewhere a template class (an allocator):
template<class Arena, typename T> Allocator
{
public:
Allocator(Arena a);
...
};
Then I have another class, which is in some kind of relation with Allocator, but can't specialize T param in instantiation.
The idea is declare a "not specialized alias" for it, and let the specialization be somewhere else.
Something like:
class MemoryArena
{
using Allocator<T> = AnAllocatorStrategy<T>; //not legal, but just to give the idea
void* Allocate(size_t);
};
The usage should be something like:
Arena anArena(...);
Arena::Allocator<int> allocInt; // some way declare a specialization of the alias
std::vector<int, Arena::Allocator<int>> myVec(allocInt);
The main idea behind that, is that because of stl container need a specialized allocator on the contained element's type, I can still use the same memory arena and let the specialized instances to forward the allocate call to it.
But I still would like that is the Arena itself controlling the base allocation strategy (eventually also parametrazing the not completely specialized AllocatorStrategy ).
Any hint on this? Is this possible?
EDIT
Thanks to the comment I fixed the declaration using alias templates this way:
class MemoryArena
{
template<typename T> using MyAllocatorTemplate = Allocator<MemoryArena, T>;;
void* Allocate(size_t);
};
I still need to figure out if it's possible make Allocator be part of MemoryArena definition, without fully specify T parameter. Something like:
template<class AllocatorType<MemoryArena,?> = DefaultAllocType<MemoryArena,?>> //is possible not to specify T on MemoryArena definition?
class MemoryArena<AllocatorType>
{
template<typename T> using MyAllocatorTemplate = AllocatorType<MemoryArena,T>;
Alternatively, any other suggestion is appreciated. The main goal is to let the user specify the Arena's allocator type except for the T parameter (useful only for using the allocator inside containers).
template<template<class...>class Z, class...T0s>
struct partial_apply {
template<class...T1s>
using result = Z<T0s...,T1s...>;
};
template<template<class...>class A, template<class...>class B>
struct same_template : std::false_type {};
template<template<class...>class A>
struct same_template<A,A> : std::true_type {};
template<class...>class Default {};
template<
template<class...>class AllocatorType=Default
>
class MemoryArena {
template<class T>
using MyAllocatorTemplate = std::conditional_t<
same_template<AllocatorType, Default>{},
DefaultAllocType<MemoryArena, T>,
AllocatorType<T>
>;
};
suppose there is some other alloc type besides DefaultAllocType, OtherAllocType<Chicken, T>. Then:
template<class...Ts>
using OtherAllocChicken = partial_apply<OtherAllocType, Chicken>::template result<Ts...>;
using ChickenArena = MemoryArena< OtherAllocChicken >;
on the other hand, if you require that the argument passed to the incoming template be MemoryArena itself sometimes, things get more complex.
Before MemoryArena is parameterized, the type doesn't exist. So you have to have some kind of placeholder that can tell you where you should inject it.
If you want it to always be MemoryArena, then things are easier: you already have that solution:
template<template<class...>class AllocatorType = DefaultAllocType>
class MemoryArena {
template<typename T>
using MyAllocatorTemplate = AllocatorType<MemoryArena,T>;
};
I know this is a simple question but I just could not find the answer.
I am trying to do something like this but instead of with std::vector ultimately I want it to be std::shared_ptr or std::weak_ptr:
template <int dim, class ChunkClass, class PtrClass>
class BaseChunkWindow : public IChunkWindow<BaseChunkWindow<dim, ChunkClass, PtrClass>, IChunk<ChunkClass>> {
public:
...
private:
PtrClass< IChunk<ChunkClass> > ptr; <-- compiler doesn't like this line, however IChunk<ChunkClass>* works
};
It depends on what you are passing it to, if the template you're trying to instantiate takes as a parameter a class template accepting 2 (or in c++11 a variadic number of) types then you can pass std::vector to that. In most cases however, templates require types as parameters and you cannot pass the class template std::vector.
template <class T>
struct gimme_a_type{};
template <template <class,class> class T>
struct gimme_a_template{};
gimme_a_type<std::vector> //<-- does not compile, expected a type, got a template
gimme_a_type<std::vector<int> > //<-- compiles, expected a type, got a type
gimme_a_template<std::vector> //<-- compiles, expected a template, got a template that has the correct signature
gimme_a_template<std::vector<int> > //<-- does not compile, expected a template, got a type
In response to your edit, there are difficulties to using class templates as template parameters. Matching the number of parameters exactly is actually difficult to do when you have default arguments in the class template you're trying to pass (std::vector in our case).
Notice that the example above required a class template that takes 2 types, not just one. This is because std::vector takes two parameters, the second is just defaulted to std::allocator<T> for us.
The following example demonstrates the issue:
template <template <class, class> class Tem>
struct A
{
Tem<int> v; //<-- fails to compile on gcc, Tem takes two parameters
Tem<int, std::allocator<int> >; //<-- compiles, but requires a priori knowledge of Tem
};
template <template <class...> class Tem>
struct A2
{
Tem<int> v; //<-- This C++11 example will work, but still isn't perfect.
};
The C++11 example is better, but if someone passed a class that has as a signature template <class, bool = false> class A3 it fails again because A2 requires a class template that takes types and not a mix of types and non-types (false being the non-type template parameter in this example). So even though A3<int> would be a valid instantiation you couldn't pass that class to A2.
The solution there is to always use types in template parameter lists and use the std::integral_constant wrapper template to pass integral constants around.
There are a couple ways of doing it.
The limited way would be to use a template template parameter with just a limited number of parameters being passed, e.g. 3.
template<template<class,class,class> class Cont, class T, class V, class U>
void f(Cont<T,V,U>&& cont) {
//...
}
However that's pretty limiting and can be hard to manage if you decide to change it in the future.
So you can do it like so with the new Variadic Templates in C++11:
template<template<class...> class Cont, typename F, typename... Rest>
void f(Cont<F, Rest...>&& cont) {
//...
}
This would work on other containers or things and is probably much easier to manage.
When is it practical to pass traits as template parameters rather than simply using some existing traits struct like
typedef basic_ofstream< char, char_traits<char> >
vs.
typedef basic_ofstream< char >?
I have some tile classes which I would like to have some common ground (traits), so I designed tile_traits to contain all the basic info about a tile, such as int_type and flag_type, like so:
//unspecialized
template<typename T> struct tile_traits;
//... other stuff here, declaration of a tile class
template<>
struct tile_traits<tile_class>
{
typedef tile_class::int_type int_type;
typedef tile_class::flag_type flag_type;
//other possible tile info here.
}
Is designing traits as such considered a traits-blob?
The design of traits is as much art as anything else. There are no hard and
fast answers here. I believe this question has gone unanswered because it is
impossible to give a good answer without knowing a lot more about the problem
you are solving.
In general traits classes are a useful "customization point". That is, if you
are designing a template:
template <class Tile>
class TileContainer
{
...
};
TileContainer might make use of tile_traits<Tile> for some properties of Tile.
And the client of TileContainer can specialize tile_traits<MyTile> in order to
communicate variations of the properties when the default trait (if it exists)
is not correct.
So far I don't think I've said anything you don't already know (judging from
the way your question is worded).
I think your question is:
Should you design:
A)
template <class Tile, class Traits = tile_traits<Tile>>
class TileContainer
{
// uses Traits
};
or:
B)
template <class Tile>
class TileContainer
{
// uses tile_traits<Tile>
};
There are examples of both designs in the C++03 and upcoming C++0x standards.
Example A designs:
template<class charT, class traits = char_traits<charT>,
class Allocator = allocator<charT>>
class basic_string; // both traits and Allocator are traits
template <class Codecvt, class Elem = wchar_t,
class Tr = char_traits<Elem>>
class wbuffer_convert;
template <class T, class Allocator = allocator<T>>
class vector; // Allocator is a A-trait that uses another
// B-trait internally: allocator_traits<Allocator>
template <class charT, class traits = regex_traits<charT>>
class basic_regex;
Example B designs:
template<class Iterator> struct iterator_traits;
template <class Alloc> struct allocator_traits;
template <class Ptr> struct pointer_traits;
template <class Rep> struct treat_as_floating_point;
template <class Rep> struct duration_values;
My only advice is that there is no right or wrong design. Use:
template <class Tile>
class TileContainer
{
// uses tile_traits<Tile>
};
when you are sure that your customer's needs can always be met by specializing
tile_traits<MyTile>.
Use:
template <class Tile, class Traits = tile_traits<Tile>>
class TileContainer
{
// uses Traits
};
when you suspect that your customer may need different traits for the same
Tile, or when you want to force the type of TileContainer to be different when
some trait other than tile_traits is used.
You need to have the traits class as a template parameter if you can see that people would pass different traits for the same data type. If your tiles will always have the same tile_traits for each T, you can use that directly.
If you can see that someone, sometimes, will use a my_special_traits, you need to have that as a separate template parameter.
Seeing you can provide default values for the traits, and having the traits parameter is always more flexible, I would choose this approach unless you have some specific reasons why you cannot do it.
template<class Bar,class Traits=FooTraits<Bar> >
class Foo
{};
I am pondering about partial specialization. While I understand the idea, I haven't seen any real-world usage of this technique. Full specialization is used in many places in STL so I don't have a problem with that. Could you educate me about a real-world example where partial specialization is used? If the example is in STL that would be superior!
C++0x comes with unique_ptr which is a replacement for auto_ptr which is going to be deprecated.
If you use unique_ptr with an array type, it uses delete[] to free it, and to provide operator[] etc. If you use it with a non-array type, it uses delete. This needs partial template specialization like
template<typename T>
struct my_unique_ptr { ... };
template<typename T>
struct my_unique_ptr<T[]> { ... };
Another use (although a very questionable) is std::vector<bool, Allocator> in the standard library. The bool specialization uses a space optimization to pack bools into individual bits
template<typename T, typename Allocator = std::allocator<T> >
struct vector { ... };
template<typename Allocator>
struct vector<bool, Allocator> { ... };
Yet another use is with std::iterator_traits<T>. Iterators are required to define the nested typedefs value_type, reference and others to the correct types (for a const iterator, reference would usually be T const&, for example) so algorithms may use them for their work. The primary template uses type-members of the iterator type in turn
template<typename T>
struct iterator_traits {
typedef typename T::value_type value_type;
...
};
For pointers, that of course doesn't work. There is a partial specialization for them
template<typename T>
struct iterator_traits<T*> {
typedef T value_type;
...
};
In some stl implementations collections like std::vector and std::list use partial template specialization to reduce the amount of code generated for collections of pointers.
Each instantiation of a template for a type T creates new code. However pointer types are effectively all the same so generating new code for every type is a waste. This can be reduced by implementing the private part of pointer collections with void pointers and then casting these to the appropriate type in the public interface. This greatly reduces the code generated for pointer collections.
I think this is covered in Effective STL.
Taken from MSDN (Partial Specialization of Class Templates (C++))
// partial_specialization_of_class_templates.cpp
template <class T> struct PTS {
enum {
IsPointer = 0,
IsPointerToDataMember = 0
};
};
template <class T> struct PTS<T*> {
enum {
IsPointer = 1,
IsPointerToDataMember = 0
};
};
template <class T, class U> struct PTS<T U::*> {
enum {
IsPointer = 0,
IsPointerToDataMember = 1
};
};