Templated list of templated objects in C++ - c++

I'm trying to create a list of objects, where each object also stores 'ownership' - ie, it knows which list holds it.
In a non-template situation, it's straightforward:
class FooList; // forward declaration
class FooItem
{
public:
FooList *owner;
...
};
class FooList: private std::list<FooItem>
{
...
};
However, the list class is a template, based on the contained object type, and I'm struggling to work out how to specify this. I reckon the FooItem class now needs to be a template because the type of 'owner' can vary:
template <class E> class FooItem
{
public:
std::list<E> *owner;
};
template <class E> class FooList: private std::list<E>
{
...
};
But, now given my two templates, how can I define the new types I want? The snippet below is what I think I need, but it gives "error: Multiple declaration for BarItem".
class BarItem;
typedef FooList<BarItem> BarList;
typedef FooItem<BarList> BarItem;
EDIT:
Thanks to those who pointed out the issue of std::list<E> instead of std::list<FooItem<E> >
EDIT 2:
Renamed classes to Base, Derived, BaseList, DerivedList.
My real problem was the 'circular typedef'. After some more tinkering, I think this will do what I require. It creates a 'real' BarItem class rather than just a typedef, but seems to at least compile now.
template <class E> class BaseList; // forward declaration
template <class E> class Base
{
public:
BaseList< Base<E> > *owner;
};
template <class E> class BaseList: private std::list< E >
{
};
// typedef Base<BaseList<Derived> > Derived; //This won't compile, unsurprisingly.
class Derived : public Base < BaseList<Derived> > // Surprisingly, this seems to.
{
...
};
typedef BaseList<Derived> DerivedList;
Does this seem to make sense? Is it a common idiom or something horrible?

Are you sure you didn't want:
template <class E> class FooItem
{
public:
std::list< FooItem<E> > *owner; // owned by a list of FooItem<E>, not raw E
};
template <class E> class FooList: private std::list< FooItem<E> > // is a list of FooItem<E>, not raw E
{
...
};
The error you are getting is because you forward declare the class BarItem but later try to redefine that name using typedef. Not sure what you're trying to accomplish, but you'll need to introduce a third name. Maybe you meant:
class Bar;
typedef FooItem<Bar> BarItem;
typedef FooList<Bar> BarList;
EDIT: The new code you've posted certainly compiles, but seems very awkward (for one thing, the naming seems really confusing). Perhaps you should ask a new question with a more concrete example of why you think you need an 'item which is an item of lists of itself' and see if others can come up with a less awkward design.

Is that last set of typedef statements correct? You posted this:
class BarItem;
typedef FooList<BarItem> BarList;
typedef FooItem<BarList> BarItem;
It's somewhat recursive, right? The first statement says there's a class BarItem that exists elsewhere. The second statement says that the type FooList<BarItem> (a list of BarItem objects) can also be referred to as BarList. The third statement says that the type FooItem<BarList> can also be referred to as BarItem, but BarItem was already defined as a type by the class BarItem statement.
So you're saying that BarItem is a type all to itself (via the class statement), but you're also saying that BarItem is an alias for the type FooItem<BarList>. Hence the conflict.

If you just want to generalize your first approach to a templated FooItem class, it would look like this:
template <class E> class FooList; // forward declaration
template <class E> class FooItem
{
public:
FooList<E> *owner;
...
};
template <class E> class FooList: private std::list< FooItem<E> >
{
...
};
If that is not what you want I'm not sure what exactly you are attempting to do.

Related

Template of a class that contains a constant-type template?

Situation
I'm trying to implement a container that holds a specific data type - let's call it C. The container(let's call it B) is an inner class of A. I'm trying to declare the template but am running into compiler issues and am not sure what I should do.
Attempts
template <typename T<C>>
class A
{
class B
{
typedef std::unique_ptr<T> containerPtr;
private:
containerPtr container;
}
}
typedef std::shared_ptr<A<std::vector<C>>> somePtr; // Error!
The error is:
struct C
type name is not allowed
template <typename T,U>
class A
{
class B
{
typedef std::unique_ptr<T<U>> containerPtr;
private:
containerPtr container; // But does it contain C or some other type now?
// We have to do a check - what's the best approach?
}
}
typedef std::shared_ptr<A<std::vector<C>>> somePtr;
What is the best approach in this situation?
If you know for a fact that T is a template container storing some type C, then you don't need to specify C anywhere and can just templatize over T:
template <typename T>
class A {
class B {
/* Use the type name T however you'd like. */
};
};
This works because T has to be the name of a complete type, so if you do something like
A<std::vector<int>>
then T is std::vector<int> and any time you use T it will specifically be a std::vector of ints and not of any other type.
On the other hand, if you want the client to provide the name of a template class and then forcibly instantiate it with your choice of C, you can use template template arguments, like this:
template <template <typename...> class T>
class A {
class B {
typedef std::unique_ptr<T<C>> containerPtr;
/* ... use containerPtr ... */
};
};
This asks the user to give you a template type, so you'd write something like
A<std::vector> myObject;
and your A template will then instantiate std::vector using the type C.

Nested Template Issue C++

I have a templated node class declared as
template <class T_>
class Node : public std::iterator<std::bidirectional_iterator_tag, T_, T_>
{
...
}
Which works fine, and I'm trying to take this existing class and apply it to a new linked list class.
I'd like to create it by calling something along the lines of
LinkedList<float> nodeList;
where it implicitly takes the type given, and within the structure of the class handles it as Node of type T_.
Is there a way to do this without making the node a sub-class of LinkedList?
Is this what you are asking?
template <class T_>
class LinkedList
{
public:
typedef Node<T_> node_type;
void AddNode(node_type *node)
{ /*...*/ }
};
Technically, the typedef is not necessary, but IME it is a good pratice to typedef the dependent types in this way.
You can provide the iterator as a default template argument:
template<
class T,
class Iterator = Node<T>
> class LinkedList
{
// ...
};
If i understood your question correct, you need to instantiate a class template using templated class type. Use this syntax:
LinkedList<Node<float> > nodeList;
(notice a space between > >; it is always a good idea to add it, because old versions of some compilers have troubles on distinguishing such constructs from shift operators >>)

How to properly declare a self-referencing template type?

How do I declare a templated type that refers to itself?
template <class T = Animal> class Animal
{
public:
T getChild ();
}
With this, I get a compiler error concerning a missing type specifier. I tried to forward-declare Animal, without success.
I am trying to impose a type constraint. A Lion can only have a Lion as a child, a Bear has a Bear, and so on.
EDIT
I'll post part of the actual class. It is a template for classes that can appear in a linked list:
template <class T = Linked<T> > class Linked
{
private:
T* m_prev;
T* m_next;
}
I want to enforce that the class can only point to object of the same class (or a subclass).
In this case, you need to specify some type parameter to Animal in your typename definition, or else it would be an "infinite recursion" in the type construction:
template<class T> class Animal;//you'll need this forward declaration
template <class T = Animal<int> > class Animal //int is just an example
{
public:
T getPrey ();
}
The OP has been answered but I want to chime in because the immediate cause of the problem is not recursion, as others claim. The simplest reason this wouldn't work is that class templates are not types. They are templates. Similarly, function templates are not functions either. So all of this is nonsensical:
template<typename T> int function_template(int);
typedef int function_type(int);
void eats_a_function(function_type&); // needs a reference to function
std::vector< std::vector > vec0; // std::vector is not a type
std::vector< std::list > vec1; // std::list is not a type
eats_a_function(function_template); // function_template is not a function
Notice that in the vec1 case, std::list is not related to std::vector. The template is fully defined (assuming header inclusion) at the point of instantiation. It still won't work.
Instead, the following works:
std::vector< std::vector<int> > vec2; // std::vector<int> is a type
std::vector< std::list<double> > vec3; // std::list<double> is a type
eats_a_function(function_template<long>); // function_template<long> is a function
Notice that in the vec2 case, it's fine to pass an instantiation of the template itself.
For the record, a toy solution to the toy problem on writing a template that refers to itself, using the proverbial layer of indirection:
// expects a template that expects a type
template<template<class> class T> struct indirection {};
// forward decl. for defaulting the parameter
template<typename T> struct recursive;
// template that expects a type
template<typename T = indirection<recursive> > struct recursive {};
Not terribly powerful given the few things that are possible with a template (the T parameter inside indirection). It's of course possible to write a rebind-style metafunction that returns an instantiation of T.
The usual way to do something like a linked list is:
template <class T> class Linked
{
private:
Linked<T>* m_prev;
Linked<T>* m_next;
}
Does this work for you, and if not, what are you trying to accomplish that can't be done this way?
You can't create a template class wherein the template type is the class itself. This is a logical recursion that your compiler can't mitigate. Since templates require the compiler to construct the object when a specific typing is encountered ( say Animal ), you have to have a complete definition of the template type. Otherwise, the compiler will recursively try to construct objects. Since your object self-references, this will be an non-terminating recursion.
This use-case seems much more appropriate for inheritance than for templates.
Doing this will let you compile:
template class Animal
{
public:
T getPrey ();
}
Or, if you really wan't a default argument:
template <class T=Cheetah> class Animal
{
public:
T getPrey ();
}
Yet, cheetah must not use itself or Animal as one of its potential template types.

C++: Default values for template arguments other than the last ones?

I have my templated container class that looks like this:
template<
class KeyType,
class ValueType,
class KeyCompareFunctor = AnObnoxiouslyLongSequenceOfCharacters<KeyType>,
class ValueCompareFunctor = AnObnoxiouslyLongSequenceOfCharacters<ValueType>
>
class MyClass
{
[...]
}
Which means that when I instantiate an object of this class, I can do it several different ways:
MyClass<MyKeyType, MyValueType> myObject;
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor> myObject;
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor, MyCustomValueCompareFunctor> myObject;
Those are all good. The problem comes when I want to instantiate a MyClass that uses a non-default version of the ValueCompareFunctor argument, but I still want to use the default value of the KeyCompareFunctor argument. Then I have to write this:
MyClass<MyKeyType, MyValueType, AnObnoxiouslyLongSequenceOfCharacters<MyKeyType>, MyCustomValueCompareFunctor> myObject;
It would be much more convenient if I could somehow omit the third argument and just write this:
MyClass<KeyType, ValueType, MyCustomValueCompareFunctor> myObject;
Since the MyCustomValueCompareFunctor works only on objects of type MyValueType and not on objects of type MyKeyType, it seems like the compiler could at least theoretically work out what I meant here.
Is there a way to do this in C++?
In general, both in templates and functions or methods, C++ lets you use default for (and thereby omit) only trailing parameters -- no way out.
I recommend a template or macro to shorten AnObnoxiouslyLongSequenceOfCharacters<MyKeyType> to Foo<MyKeyType> -- not perfect, but better than nothing.
No. The closest you can come is to allow users to specify some sentinel type - like void - meaning "use default value here", and use template metamagic inside your class to typedef the real default if void was given to you. But this probably isn't a good idea from readability point of view.
Boost parameters and Boost graph named parameters are efforts towards naming parameters for template functions/methods. They give the opportunity to provide arguments in whichever order you prefer. Some arguments may be optional, with default values.
The same approach may be applied to template arguments. Instead of having N template arguments + P optional ones, create your class with N+1 template arguments. The last one will hold "named" parameters which can be omitted.
This answer is not complete yet, but i hope it's a good start !
An alternative option is to use Traits classes:
template <class KeyType>
class KeyTraits
{
typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType> Compare;
};
template <class ValueType>
class ValueTraits
{
typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType> Compare;
};
template<class KeyType class ValueType>
class MyClass
{
typedef KeyTraits<KeyType>::Compare KeyCompareFunctor;
typedef ValueTraits<ValueType>::Compare KeyCompareFunctor;
};
Then if you have a type which needs a different comparison function for Key's, then you'd explicitly specialize the KeyTraits type for that case. Here's an example where we change it for int:
template <>
class KeyTraits<int>
{
typedef SpecialCompareForInt Cmopare;
};
There is another option, which uses inheritance and which works like the following. For the last two arguments, it uses a class that inherits virtually from a class that has two member templates, that can be used to generate the needed types. Because the inheritance is virtual, the typedefs it declares are shared among the inheritance as seen below.
template<class KeyType,
class ValueType,
class Pol1 = DefaultArgument,
class Pol2 = DefaultArgument>
class MyClass {
typedef use_policies<Pol1, Pol2> policies;
typedef KeyType key_type;
typedef ValueType value_type;
typedef typename policies::
template apply_key_compare<KeyType>::type
key_compare;
typedef typename policies::
template apply_value_compare<ValueType>::type
value_compare;
};
Now, have a default argument that you use, which has typedefs for the default arguments you want provide. The member templates will be parameterized by the key and value types
struct VirtualRoot {
template<typename KeyType>
struct apply_key_compare {
typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType>
type;
};
template<typename ValueType>
struct apply_value_compare {
typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType>
type;
};
};
struct DefaultArgument : virtual VirtualRoot { };
template<typename T> struct KeyCompareIs : virtual VirtualRoot {
template<typename KeyType>
struct apply_key_compare {
typedef T type;
};
};
template<typename T> struct ValueCompareIs : virtual VirtualRoot {
template<typename ValueType>
struct apply_value_compare {
typedef T type;
};
};
Now, use_policies will derive from all the template arguments. Where a derived class of VirtualRoot hides a member from the base, that member of the derived class is dominant over the member of the base, and will be used, even though the base-class member can be reached by other path in the inheritance tree.
Note that you don't pay for the virtual inheritance, because you never create an object of type use_policies. You only use virtual inheritance to make use of the dominance rule.
template<typename B, int>
struct Inherit : B { };
template<class Pol1, class Pol2>
struct use_policies : Inherit<Pol1, 1>, Inherit<Pol2, 2>
{ };
Because we potentially derive from the same class more than once, we use a class template Inherit: Inheriting the same class directly twice is forbidden. But inheriting it indirectly is allowed. You can now use this all like the following:
MyClass<int, float> m;
MyClass<float, double, ValueCompareIs< less<double> > > m;

Template typedef error

Can anyone explain why this code gives the error:
error C2039: 'RT' : is not a member of 'ConcreteTable'
(at least when compiled with VS2008 SP1)
class Record
{
};
template <class T>
class Table
{
public:
typedef typename T::RT Zot; // << error occurs here
};
class ConcreteTable : public Table<ConcreteTable>
{
public:
typedef Record RT;
};
What can be done to fix it up. Thanks!
Update:
Thanks pointing out the issue, and for all the suggestions. This snippet was based on code that was providing an extensibility point within an existing code base, and the primary design goal was reducing the amount of work (typing) required to add new extensions using this mechanism.
A separate 'type traits' style class actually fits into the solution best. Especially as I could even wrap it in a C style macro if the style police aren't looking!
That's because the class ConcreteTable is not yet instantiated when instantiating Table, so the compiler doesn't see T::RT yet. I'm not really sure how exactly C++ standard handles this kind of recursion (I suspect it's undefined), but it doesn't work how you'd expect (and this is probably good, otherwise things would be much more complicated - you could express a logical paradox with it - like a const bool which is false iff it is true).
Fixing
With typedefs, I think you cannot hope for more than passing RT as additional template parameter, like this
template <class T, class RT>
class Table
{
public:
typedef typename RT Zot;
};
class ConcreteTable : public Table<ConcreteTable, Record>
{
public:
typedef Record RT;
};
If you don't insist on RT being available as Table<>::Zot, you can put it inside a nested struct
template <class T>
class Table
{
public:
struct S {
typedef typename RT Zot;
};
};
class ConcreteTable : public Table<ConcreteTable>
{
public:
typedef Record RT;
};
Or even external traits struct
template <class T>
struct TableTraits<T>;
template <class T>
struct TableTraits<Table<T> > {
typedef typename T::RT Zot;
};
If you only want the type be argument/return type of a method, you can do it by templatizing this method, eg.
void f(typename T::RT*); // this won't work
template <class U>
void f(U*); // this will
The point of all these manipulations is to postpone the need for T::RT as late as possible, particularly till after ConcreteTable is a complete class.
Why not just do something like this?
class Record
{
};
template <class T>
class Table
{
public:
typedef typename T Zot;
};
class ConcreteTable : public Table<Record>
{
public:
typedef Record RT; //You may not even need this line any more
};
The problem is that ConcreteTable is defined in terms of Table, but you can't define Table without a definition of ConcreteTable, so you've created a circular definition.
It also looks like there may be an underlying problem in the way you are designing your class hierarchy. I am guessing what you are trying to do is provide ways of manipulating a generic record type in your definition of Table, but leaving it up to ConcreteTable to define what the record type is. A better solution would be to make the record type a parameter of the Table template, and ConcreteTable a direct subclass:
class Record {};
template <class T>
class Table {
public:
typedef T RT;
};
class ConcreteTable : public Table<Record> {
};
Now you eliminate the circular dependency, and Table is still abstracted based on the type of record.
Notice that what you want to do can't be allowed by the compiler. If it was possible, you would be able to do this:
template <class T>
class Table
{
public:
typedef typename T::RT Zot;
};
class ConcreteTable : public Table<ConcreteTable>
{
public:
typedef Zot RT;
};
Which would be a kind of type-definition-infinite-loop.
The compiler blocks this possibility by requiring a class to be fully defined when one of its members needs to be used; in this case, the point of template instatiation for Table (the ancestors list in ConcreteTable) is before the definition of RT, so RT can't be used inside Table.
The workaround requires having an intermediate class to avoid the mutual dependence, as others already stated.
When Table<ConcreteTable> is instantiated, ConcreteTable is still an incomplete type. Assuming you want to stick with CRTP you could just pass Record as an additional template parameter like:
class Record
{
};
template <class T, class U>
struct Table
{
typedef U RT;
};
struct ConcreteTable : Table<ConcreteTable, Record>
{
};
Also note that you can access ConcreteTable as a complete type in any member functions in Table because they are instantiated only later when used. So this would be ok:
struct Record
{
};
template <class T>
struct Table
{
void foo()
{
typedef typename T::RT Zot;
Zot a; // ...
}
};
struct ConcreteTable : Table<ConcreteTable>
{
typedef Record RT;
};
int main()
{
ConcreteTable tab;
tab.foo();
}
I think everyone else has covered it pretty well, I just wanted to add that I think it's bad practice to inherit from a template of self and then try to patch things round to make it work. I would take a different approach and have the record type (RT) as the parameters instead of ConcreteTable itself. If you've ever looked at the std::iterator class, it uses this exact approach:
template <class Category, class T, class Distance = ptrdiff_t,
class Pointer = T*, class Reference = T&>
struct iterator {
typedef T value_type;
typedef Distance difference_type;
typedef Pointer pointer;
typedef Reference reference;
typedef Category iterator_category;
};
When a subclass inherits from iterator, it does this:
struct ExampleIterator : std::iterator<std::forward_iterator_tag, Example>
Which is exactly what you want to do. Notice that the 'RecordType' fields are actually in the superclass, and passed in through template parameters. This is the best way to do it, it's in the standard library because of it.
If you want to do more specialisation of the ConcreteTable subclass, you can always override methods from Table, as well as using the template parameters.
you are trying to use class CponcreateTable as a template parameter before the class is fully defined.
The following equivalent code would work just fine:
class Record
{
};
template <class T> Table
{
public:
typedef typename T::RT Zot; // << error occurs here
};
class ConcreteTableParent
{
public:
typedef Record RT;
};
class ConcreteTable: public Table<ConcreteTableParent>
{
public:
...
};