is it possible in C++ to create an alias of template class (without specifying parameters)?
typedef std::map myOwnMap;
doesn't work.
And if not, is there any good reason?
In C++98 and C++03 typedef may only be used on a complete type:
typedef std::map<int,int> IntToIntMap;
With C++0x there is a new shiny syntax to replace typedef:
using IntToIntMap = std::map<int,int>;
which also supports template aliasing:
template <
typename Key,
typename Value,
typename Comparator = std::less<Key>,
typename Allocator = std::allocator< std::pair<Key,Value> >
>
using myOwnMap = std::map<Key,Value,Comparator,Allocator>;
Here you go :)
Template typedefs are not supported in the C++03 standard. There are workarounds, however:
template<typename T>
struct MyOwnMap {
typedef std::map<std::string, T> Type;
};
MyOwnMap<int>::Type map;
This feature will be introduced in C++0x, called template alias. It will be looking like this:
template<typename Key, typename Value>
using MyMap = std::map<Key, Value>
For C++ lower 11 only one way
template <...>
struct my_type : real_type<...> {}
An alternative approach:
template <
typename Key,
typename Value,
typename Comparator = std::less<Key>
>
class Map: public std::map<Key,Value, Comparator> {
};
Related
I have a template class that basically implements registry design pattern.
Values are registered with Keys and are stored in some container:
template <typename Key, typename Value,
template <typename...> class Container >
class registry
{
public:
typedef Key key_type;
typedef Value value_type;
typedef std::pair<key_type, value_type> element_type;
typedef Container<element_type> container_type;
Then, I can use it with sequence containers like this:
registry<const char*,void*, std::list> r1;
registry<const char*,void*, std::vector> r2;
I can even use it with alias:
template <typename T>
using alias_container = std::array<T, 10>;
registry<const char*,void*, alias_container > r4;
But I can't figure out how to use it with typedef like this:
template <typename T>
class my_container2
{
typedef std::array<T,3> type;
};
I basically want something like this:
registry<const char*,void*, my_container2::type > r5;
Thanks a lot for your help.
type is dependent on the template type provided to my_container2. In order to use it you need to specify the template parameter like
registry<const char*,void*, my_container2<some_type>::type > r4;
^^^^^^^^^ template type here
This is the same concept as iterators to standard types. You can't use container_type::iterator. You have to use container_type<some_type>::iterator.
//a class template to do something to STL container
template<typename T/*should be a STL container*/,typename Ele/*the type of element*/>
struct foo{
T a_container_with_an_element(){
T con;
Ele e;
con.push_back(++++e);
return con;
}
As you can see, it's really foolish to put the element's type into templates' parameter because it's already contained in the container's type.
So, is there any magic to get the element's type out of T?
Lots of Thx :-)
If the container is a standard library container, the name of the element is an embedded name of the container as follows:
typedef typename T::value_type type;
The standard container's have a few standard names in them (for example, see vector on cppreference) and the C++ standard ยง23.2.
X::value_type
X::reference
X::const_reference
X::iterator
X::const_iterator
X::difference_type
X::size_type
Every "container" in the standard library, tries to adhere to the Container concept. This concepts requires that, given a container T:
T::value_type is the type of the container element
T::reference is a reference type to the container element
T::const_reference is a constant reference type to the container element
In your specific example, you can extract the element type by:
template<typename Container>
struct foo {
Container a_container_with_an_element(){
Container con;
typename Container::value_type e;
con.push_back(++++e);
return con;
}
In your special case where you know that T is a STL container Naill's answer is correct, use the nested types in the container.
If however you want a more generic "I know I am getting a template as T and I want the type of the first parameter of that template" case you can get the type through template specialization:
template<typename T>
struct MyFirstElement;
template<template<typename...> class T_Container, typename T_Type, typename... Ts>
struct MyFirstElement<T_Container<T_Type,Ts...>>{
using Type = T_Type;
};
//unit test
using MustBeInt = typename MyFirstElement<std::vector<int>>::Type;
using MustBeLong = typename MyFirstElement<std::map<long,float>>::Type;
static_assert(std::is_same<MustBeInt,int>::value,"");
static_assert(std::is_same<MustBeLong,long>::value,"");
//this works too even though its not an STL container
template<typename T, typename U, typename Z>
struct MyCrazyContainer{};
static_assert(std::is_same<typename MyFirstElement<MyCrazyContainer<bool,long,float>>::Type,bool>::value,"");
here is a live example: http://ideone.com/OxhSOc
As Jarod42 pointed out this will only work with templates which are "regular" i.e. only have types as parameters. std::array for example breatks this because it has an int as a second parameter.
If T should be an STL container you could use a template-template parameter. It must be a container that supports push_back() and we will incorporate the fact that STL containers contain a 2nd "allocator" parameter.
In that case you can use
template< template<typename, typename> class Con,
typename Ele, typename Alloc = std::allocator<Ele> >
struct foo
{
typedef Con<Ele, Alloc> container_type;
typedef Ele value_type;
container_type a_container_with_an_element()
{
container_type con;
value_type e;
con.push_back( e ); // or modify e first but not ++++e
return con;
}
};
Your code would then do
foo< std::vector, int > myfoo;
std::vector<int> myvec = myfoo.a_container_with_an_element();
If you want just one template parameter
template< typename Con >
struct foo
{
typedef typename Con::value_type value_type;
Con a_container_with_an_element()
{
Con con;
value_type e;
con.push_back( e ); // or modify e first but not ++++e
return con;
}
};
and usage:
foo<std::vector<int> > myfoo;
std::vector<int> myvec = myfoo.a_container_with_an_element();
Note that in this example you can also do foo<std::vector<int> >::Ele to get the element type. You could make a typedef for the container type too. Often used in templates. (You need typename when the template is not fully qualified. So foo<std::vector<int>> is fully qualified and doesn't need it there.
I was wondering if it is possible to have some kind of parameterized typedef.
To illustrate, in my code I use this typedef:
typedef std::queue<std::vector<unsigned char>, std::deque<std::vector<unsigned char> > > UnsignedCharQueue;
As you can see this is a rather unwieldy construct so the typedef makes sense. However, if I want to have queues with other datatypes I need to define them beforehand explizitly.
So I was thinking if it were possible to use a construct like this:
typedef std::queue<std::vector<T>, std::deque<std::vector<T> > > Queue<T>;
private:
Queue<unsigned char> mMyQueue;
Similar like generics in Java.
In C++11, you can use template aliases, such as in:
template<typename T>
using my_alias = some_class_template<T>;
// ...
my_alias<T> obj; // Same as "some_class_template<T> obj;"
So in your case it would be:
template<typename T>
using Queue = std::queue<std::vector<T>, std::deque<std::vector<T> > >;
Also notice, that in C++11 you do not need to leave a space between closed angle brackets, so the above can be rewritten as follows:
template<typename T>
using Queue = std::queue<std::vector<T>, std::deque<std::vector<T>>>;
// ^^^
In C++03 you could define a Queue metafunction this way:
template<typename T>
struct Queue
{
typedef std::queue<std::vector<T>, std::deque<std::vector<T> > > type;
};
Which you would then use this way:
Queue<int>::type obj;
If you are using it in a template with parameter T (as in the following), do not forget the typename disambiguator:
template<typename T>
struct X
{
typename Queue<T>::type obj;
// ^^^^^^^^
}
Yes, it works like this:
template <typename T> using Queue = std::queue<std::vector<T>, std::deque<std::vector<T> > >;
The following code represents a container based on std::vector
template <typename Item>
struct TList
{
typedef std::vector <Item> Type;
};
template <typename Item>
class List
{
private
typename TList <Item>::Type items;
....
}
int main()
{
List <Object> list;
}
Is it possible to templatize std::vector and create a general container, something like that?
template <typename Item, typename stl_container>
struct TList
{
typedef stl_container<Item>;
};
where stl_container represents std::vector, std::list, std::set...? I would like to choose the type of container at the time of the creation.
List <Object, std::vector> list; //vector of objects, not a real code
List <Object, std::vector> list; //list of objects, not a real code
Thanks for your answers...
Updated question:
I tried the following code but there are errors:
#include <vector>
template <typename Item, typename Container>
struct TList
{
typedef typename Container <Item>::type type; //Error C2059: syntax error : '<', Error C2238: unexpected token(s) preceding ';
};
template <typename T>
struct vector_container
{
typedef std::vector<T> type;
};
int _tmain(int argc, _TCHAR* argv[])
{
TList <int, vector_container> v;
TList <int, map_container> m;
}
Yes, but not directly:
template <typename Item, template <typename> class Container>
struct TList
{
typedef typename Container<Item>::type type;
};
Then you can define different container policies:
template <typename T>
struct vector_container
{
typedef std::vector<T> type;
};
template <typename T>
struct map_container
{
typedef std::map<T, std::string> type;
};
TList<int, vector_container> v;
TList<int, map_container> m;
A bit verbose, though.* To do things directly, you'd need to take the route described by James, but as he notes this is ultimately very inflexible.
However, with C++0x we can do this just fine:
#include <map>
#include <vector>
template <typename Item,
template <typename...> class Container, typename... Args>
struct TList
{
// Args lets the user specify additional explicit template arguments
Container<Item, Args...> storage;
};
int main()
{
TList<int, std::vector> v;
TList<int, std::map, float> m;
}
Perfect. Unfortunately there's no way to reproduce this in C++03, except via the indirection policy classes introduce as described above.
*I want to emphasize that by "A bit verbose" I mean "this is unorthodox". The correct solution for your problem is what the standard library does, as Jerry explains. You just let the user of your container adapter specify the entire container type directly:
template <typename Item, typename Container = std::vector<Item>>
struct TList
{};
But this leaves a big problem: what if I don't want the value type of the container to be Item but something_else<Item>? In other words, how can I change the value type of an existing container to something else? In your case you don't, so read no further, but in the case we do, we want to rebind a container.
Unfortunately for us, the containers don't have this functionality, though allocators do:
template <typename T>
struct allocator
{
template <typename U>
struct rebind
{
typedef allocator<U> type;
};
// ...
};
This allows us to get an allocator<U> given an allocator<T>. How can we do the same for containers without this intrusive utility? In C++0x, it's easy:
template <typename T, typename Container>
struct rebind; // not defined
template <typename T, typename Container, typename... Args>
struct rebind<T, Container<Args...>>
{
// assumes the rest are filled with defaults**
typedef Container<T> type;
};
Given std::vector<int>, we can perform rebind<float, std::vector<int>>::type, for example. Unlike the previous C++0x solution, this one can be emulated in C++03 with macros and iteration..
**Note this mechanism can be made much more powerful, like specifying which arguments to keep, which to rebind, which to rebind themselves before using as arguments, etc., but that's left as an exercise for the reader. :)
I'm a bit puzzled why some very smart (and competent) people are saying no.
Unless I've misread your question, what you're trying to accomplish is virtually identical to the "container adapters" in the standard library. Each provides an interface to some underlying container type, with the container type that will be used provided as a template parameter (with a default value).
For example, a std::stack uses some other container (e.g., std::deque, std::list or std::vector) to hold the objects, and std::stack itself just provides a simplified/restricted interface for when you just want to use stack operations. The underlying container that will be used by the std::stack is provided as a template parameter. Here's how the code looks in the standard:
namespace std {
template <class T, class Container = deque<T> >
class stack {
public:
typedef typename Container::value_type value_type;
typedef typename Container::size_type size_type;
typedef Container container_type;
protected:
Container c;
public:
explicit stack(const Container& = Container());
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
value_type& top() { return c.back(); }
const value_type& top() const { return c.back(); }
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_back(); }
};
}
Of course, perhaps I've just misunderstood the question -- if so, I apologize in advance to the people with whom I'm (sort of) disagreeing.
Yes and no.
You can use a template template parameter, e.g.,
template <typename Item, template <typename> class Container>
struct TList { /* ... */ };
However, in general, template template parameters are not particularly useful because the number and types of the template parameters have to match. So, the above would not match std::vector because it actually has two template parameters: one for the value type and one for the allocator. A template template parameter can't take advantage of any default template arguments.
To be able to use the std::vector template as an argument, TList would have to be declared as:
template <typename Item, template <typename, typename> class Container>
struct TList { /* ... */ };
However, with this template, you wouldn't be able to use the std::map template as an argument because it has four template parameters: the key and value types, the allocator type, and the comparator type.
Usually it is much easier to avoid template template parameters because of this inflexibility.
well, you can hack it up with a macro:
template <typename T, typename stl_container = std::vector<T> >
struct TList
{
typedef stl_container Type;
};
#define TLIST(T, C) TList<T, C<T> >
TList<int> foo;
TList<int, std::list<int> > bar;
TLIST(int, std::list) baz;
Is it possible to templatize std::vector and create a general container, something like that?
No. You would have to templatize the function or object using the container -- you couldn't templatize the container itself.
For example. consider a typical std::find:
template<class InputIterator, class T>
InputIterator find ( InputIterator first, InputIterator last, const T& value )
{
for ( ;first!=last; first++) if ( *first==value ) break;
return first;
}
This works for any container, but doesn't need a tempalte with the container at all.
Also, given that it looks what you're trying to do is make container independent code, you might want to buy or borrow yourself a copy of Scott Meyers' Effective STL and read Item 2: Beware the illusion of container-independent code.
You can use template template parameters as others have mentioned here. The main difficulty with this is not that dissimilar container types have dissimilar template parameters, but that the standard allows the standard containers, like vector, to have template parameters in addition to the documented, necessary ones.
You can get around this by providing your own subclass types that accept the appropriate template parameters and let any extras (which have to have defaults) be filled in my the implementation:
template < typename T > struct simple_vector : std::vector<T> {};
Or you can use the templated typedef idiom:
template < typename T > struct retrieve_vector { typedef std::vector<T> type; };
A C++ hash_map has the following template parameters:
template<typename Key, typename T, typename HashCompare, typename Allocator>
How can I specify a Allocator without specifying the HashCompare?
This won't compile :(
hash_map<EntityId, Entity*, , tbb::scalable_allocator>
The simple answer is that you can't. You cannot skip a template parameter and have it choose the default value for the template. Your only option is to find out what the default is and insert it into your declaration.
There's one trick you can use which will at least save you having to work out what the default is, but it does require that you know the name of the type as it is defined in hash_map.
The hash_map will probably be declared something like:
class allocator {};
class hash_compare {};
template<typename Key
, typename T
, typename HashCompare = hash_compare
, typename Allocator = allocator>
class hash_map
{
public:
typedef HashCompare key_compare;
// ...
};
We cannot leave out the default for the hash, but we can refer to the default using the member typedef:
hash_map<EntityId
, Entity*
, hash_map<EntityId,Entity*>::key_compare // find out the default hasher
, tbb::scalable_allocator> hm;
If you're going to use the type a lot, then create a typedef:
typedef hash_map<EntityId,Entity*>::key_compare EntityKeyCompare;
hash_map<EntityId
, Entity*
, EntityKeyCompare
, tbb::scalable_allocator> hm;
If the has map type has some public typedef for the HashCompare template parameter, you could write a meta function that uses a vanilla hash map type to get the std comparator. Something like this:
template< typename Key, typename T, typename Allocator>
struct hash_map_type {
typedef typename hash_map<Key,T>::key_compare key_compare;
typedef mash_map<Key,T,key_compare,Allocator> result_t;
};
typedef hash_map_type<int,string,my_allocator>::result_type my_hash_map;
This, however, depends on something like the above hash_map<Key,T>::key_compare being accessible.