Can I extract C++ template arguments out of a template class? - c++

Basically, given a template class like this:
template< class Value > class Holder { };
I would like to be able to discover the type Value for a given Holder class. I thought that I would be able to make a simple metafunction that takes a template template argument, like this:
template< template< class Value > class Holder > class GetValue
{
typedef Value Value;
};
And then extract out the Value type like this:
GetValue< Holder< int > >::Value value;
But instead I just get an error message pointing to the metafunction declaration:
error: ‘Value’ does not name a type
Is there any way to accomplish this kind of thing? Thanks.
[EDIT] I also get the error messages:
error: type/value mismatch at argument 1 in template parameter list for ‘template<template<class Value> class Holder> class GetValue’
error: expected a class template, got ‘Holder<int>’
Which leads me to conclude that Phil Nash is right, you can't pass a class as a template template argument.

Why don't you simply change your holder class to
template< class Value > class Holder {
typedef Value value_type;
value_type m_val; // member variable
};
In any method that consumes an object of type Holder< T > you can access the contained type like that:
template< class THolder >
void SomeMethod( THolder const& holder ) {
typename THolder::value_type v = holder.m_val;
}
This approach follows the pattern all STL classes use, e.g., std::vector< int >::value_type is int.
I think you're trying to do partial template specialization:
template<class T>
class GetValue {
};
template<class Value>
class GetValue< Holder<Value> > {
public:
typedef Value value_type;
};
In your code, you could then do the following:
template<class THolder>
void SomeMethod( THolder const& h ) {
typename GetValue< THolder >::value_type v = h.m_v;
}
In general, I'd prefer the first solution though.

template <class T> void extract_type(Holder<T>)
{
printf("%s\n", typeid(T).name());
}
I'm assuming though that you want to use that in a non-templated function. That's a bit more difficult:
template <class T> class GetValue;
template <class T> class GetValue<Holder<T> >
{
public:
typedef T value_type;
};
The magic google words are "partial template specialization"

I think you need a typename in there somewhere.
[edit]
Damn, was just going to check my suspicion that it needed to be before the GetValue, but Mykola got there first :-)
The issue is that Value is a dependent type. The compiler doesn't know whether it is meant to name a type or a value, so you need to provide the hint.
[edit2]
Oops. There is a problem with trying to answer too fast.
I missed the fact that you were trying to use a template template to achieve this.
What that allows you to do is to pass a template in instead of a type. In this case you can pass a Holder. But that doesn't help you because you want to pass a type, Holder<int>.
Looks like Mykola realised this too and removed his answer.
You'll need to use Partial Template Specialization for this. Before I had a chance to complete my own example Sebastian has beaten me to it :-)

Related

How to use member type of a template class without specifying unnecessary template parameters?

I am trying to use a member type of a template class, which does not depend on any template parameters of the template class. I would like to keep the type as the member type due to its logic, but I do not want to specify the unnecessary template parameters of the class whenever I want to use the member type outside the class.
Consider the following:
class Dummy { };
// Template class
template<typename T>
class A {
public:
template<typename T2>
class MemberType : public T2 {
public:
T2 t2;
};
};
int main()
{
typename A<Dummy>::template MemberType<Dummy> m1; // okay
typename A::template MemberType<Dummy> m2; // not okay!
return 0;
}
I got the following compiler error when I try to compile using g++:
error: ‘template<class T> class A’ used without template parameters
typename A::template MemberType<Dummy> m2; // not okay!
Is there any workaround for this?
I am trying to use a member type of a template class, which does not
depend on any template parameters of the template class.
As a nested type within class A<T>, MemberType does depend on the template parameter T.
i.e. A<T>::MemberType<T2> and A<U>::MemberType<T2> are distinct classes.
What you want to do is not possible. A template is just a template. There is very little you can do with it before actually instantiating it for a concrete type. There could be a specialization for A that has no nested MemberType at all.
I would like to keep the type as the member type due to its logic,
[...]
...but it seems the logic is something else: The MemberType does not depend on A, hence it should not be part of a template that depends on A.
Sloppy speaking template<typename T> can be read as "everything that follows depends on T". Even if you think it does not, there could always be a specialization that changes anything inside A. If you want MemberType to not depend on T then declare it outside A.
Everything in a template is dependent on the parameter(s) - meaning a template-specialization might not even have class MemberType.
But you can make a default parameter - you still need to write <> though (but you can omit template usually - even typename, but I left that):
class Dummy { };
// Template class
template <class T = void>
class A {
public:
template<typename T2>
class MemberType : public T2 {
public:
T2 t2;
};
};
int main()
{
typename A<Dummy>::MemberType<Dummy> m1; // okay
typename A<>::MemberType<Dummy> m2; // also ok
return 0;
}
As others have pointed out, this somewhat looks like an anti-pattern though - since the inner type is not dependent on the parameter of the outer template class.
Is there any workaround for this?
MemberType is a type dependent from a template parameter so, necessarily, you have to pass through the containing template a template parameter to define it
typename A<SomeType>::template MemberType<AnotherType> m2;
Taking in count that your not interested in external SomeType parameter, the best workaround I can imagine is the use of a using as follows (or something similar)
template <typename T>
using MemberType_t = typename A<T>::template MemberType<T>;
to reduce typewriting.
The following is a full compiling simplified example
#include <type_traits>
class Dummy { };
template <typename>
struct A
{
template <typename T2>
struct MemberType : public T2
{ T2 t2; };
};
template <typename T>
using MemberType_t = typename A<T>::template MemberType<T>;
int main ()
{
typename A<Dummy>::template MemberType<Dummy> m1;
MemberType_t<Dummy> m2; // compile
static_assert( std::is_same<decltype(m1), decltype(m2)>::value, "!" );
}

Nested Templates (i.e. template <typename T< typename templateArgumentFor_T >>)

I need to get 2 type-parameters for my class: T1, which is a class that has a template, and T2, which is the parameter for T1's template.
In my case, a Vertex type (there are 2, one inherits from the other), and the data type the vertex stores (name/id in my case).
I want to be able to write something like this:
template < typename VertexType < typename VertexIDType > >
(which gave me the error: C2143 syntax error: missing ',' before '<')
So that my class would be something like this:
class Graph
{
public:
Graph(const List<VertexType<VertexIDType>>& verticesList);
VertexType<VertexIDType>& getVertexByID(const VertexIDType& ID) const;
private:
List<VertexType<VertexIDType>> vertices;
};
('List' is my (not std's) implementation for a linked list.)
I also tried template <typename VertexType, typename VertexIDType>
but then I got error in the function Graph(const List<VertexType<VertexIDType>>& verticesList);
(C2947 expecting '>' to terminate template-argument-list, found '<')
and this template < typename VertexType < template <typename VertexIDType> > >
(which also gave me error C2143)
I'm really the kind of person that tries to figure everything by himself, but this is getting frustrating. I couldn't find an answer that I understood if/how to implement in my code.
I've now finished an OOP (c++) course. I have some experience with templates. I've wrote templates that get 1 or 2 arguments successfully, but nothing like this.
Please help me solve this problem, preferably as elegantly as possible :)
Thanks.
You can use template template parameters:
template <template <typename> class VertexType, typename VertexIDType>
class graph;
graph<MyVertexType, MyVertexIDType> //usage
Alternatively you could take just a type and extract the ID type in a partial specialization:
template <typename Vertex>
class graph;
template <template <typename> class VertexType, typename VertexIDType>
class graph <VertexType<VertexIDType>> {
//...
};
graph<MyVertexType<MyVertexIDType>> //usage
TartanLlama's answer is a good one for the question that you asked, but you might want to change your approach slightly. If you require that a VertexType must define a typedef VertexIDType, then you can write:
template <class VertexType>
class Graph
{
public:
Graph(const List<VertexType>& verticesList);
typedef typename VertexType::VertexIDType VertexIDType;
VertexType& getVertexByID(const VertexIDType& ID) const;
private:
List<VertexType> vertices;
};
Note the typename in the typedef for VertexIDType. It is needed to say "this name must be a type and not a variable".
Assuming your current VertexType is templated on VertexIDType:
template <classname VertexIDType>
struct VType1 {
double stuff; // Or whatever you have in your vertex
};
You would need to change it to:
template <classname VertexIDType>
struct VType1 {
double stuff;
typedef VertexIDType VertexIDType; // Provide a typedef for VertexIDType.
};
This is similar to the approach the standard library takes, where every type which is a container has a typedef for value_type etc.

Extract the generic type of another generic type in C++

Say I have a class Foo which uses two different generic types, one is _Type and the other is _Comparator. _Type is known to be a std::vector, std::list, or std::string, so it will have a type within it: T will be within vector and list; char will be within string.
My other generic type _Comparator is an optional template parameter, by which the user may specify her own less than function, functor, or lambda function. If no argument is provided as the second template parameter, it should default to the std::less<M> functor, whereby type M shall be the type of elements contained within _Type.
I do not know the syntax for how to do this.
I've tried:
template <typename _Type<T>, typename _Comparator = less<T> >
to no avail.
Using the approach mentioned by #Joachim Pileborg in the comments, I was able to come up with the following, which allowed me to access the inner type of _Type:
template <typename _Type,
typename _Comparator = less<typename _Type:: value_type> >
class Foo
{
public:
// some methods
private:
_Type sequence;
_Comparator comparator;
};
and now std::less compares the correct types without complaining.
if I understood your question correctly, you might try something like:
#include <iostream>
#include <vector>
#include <list>
#include <typeinfo>
template <typename T>
class holder
{
public:
template <typename Type = std::vector<T>, typename Comparator = std::less<T> >
class impl
{
public:
impl() {std::cout << typeid(s).name() << std::endl; }
Type s;
};
} ;
int main()
{
holder<int>::impl<> holder_of_int_vectors;
holder<int>::impl<std::list<int> > holder_of_int_lists;
holder<int>::impl<std::list<int>, std::greater<int> > holder_of_int_lists_with_greater;
}
ie, use an external class to hold the "basic" type (T) and an internal for the container (Type) and comparator.
As you said you only want to support vector, list, and string, you can use this:
template <typename T, typename Compare = std::less<typename T::value_type>>
This will support all types which have a member typedef value_type, which vector, list, and string all do.
It's possible to support other types using variadic template template parameters, but that's getting much more complicated.

Template argument deduction

I'm currently facing a problem I haven't been able to solve myself.
Basically what I'm trying to do is implement some linq-like behaviour in C++.
I'll start off with the code in my header:
template<typename T, template<class = T> class A,
template<class = T, template<class=T> class = A> class C>
class queryable
{
public:
typedef T value_type;
typedef A<value_type> allocator_type;
typedef C<value_type, allocator_type> container_type; // (1)
typedef queryable<T, A, C> type;
queryable(container_type const &) { }
template<typename _Out> queryable<_Out, A, C> select(/* some delegate */);
// more methods etc
}
And this is how I'd like it to be instantiated:
std::vector<int> my_vec;
queryable<std::vector<int> > q(my_vec);
Needless to say this doesn't work (otherwist I wouldn't be here :) )
Now the even stranger part is that even this doesn't seem to work:
std::vector<int> my_vec;
queryable<int, std::allocator, std::vector> q(my_vec);
As you can see (by looking at the select function), it is important to me to not just use something like this:
template<typename T> class queryable;
Any suggestions on how to solve this? And is this even possible?
Any help would be appreciated!
EDIT: the errors I'm getting:
../entry.cpp:19:58: error: type/value mismatch at argument 3 in template parameter list for ‘template<class T, template<class> class A, template<class, template<class> class<template-parameter-2-2> > class C> class failproof::collections::queryable’
../entry.cpp:19:58: error: expected a template of type ‘template<class, template<class> class<template-parameter-2-2> > class C’, got ‘template<class _Tp, class _Alloc> class std::vector’
../entry.cpp:19:61: error: invalid type in declaration before ‘;’ token
EDIT 2:
As far as I understand the compiler is complaining about C not taking 2 class arguments, but 1 class argument and 1 templated class argument (1), because I defined C to be that way.
Is there any way to resolve this issue?
There is a general method to 'explode' a type to test if it was created by a template, and to extract the types that were passed to that template. It is also possible to access the template itself and pass other parameters to it if you desire.
vector is a class template. When you apply parameters to it, you get something like vector<int>, which is a template class. A template class is a specific type, like any other type, it just happens to have been created via a class template.
The goal is, given a type T, to test if it is a template class, and if so to gain access to the class template that was used to create it, and also to access the parameters that were passed to the class template. In this sample, I just test for whether something is a one-arg or two-arg template, but the technique can easily be extended.
(Technically, vector is a two-arg template. There is a default for the second parameter, so vector<int> is actually vector<int, allocator<int> >, but it's still basically a two-arg template, not a one-arg template.)
The best place to start is with this sample code I've put on ideone. I'll copy the Exploder code at the end of this answer.
I begin with
typedef list<int> list_of_ints;
and proceed to use the Exploder template to access all the above information. For example, Exploder<list_of_ints> :: type_1 is the first parameter that was passed to the template, in this case int. The second parameter (this is the defaulted parameter) is allocator<int> and is accessible with Exploder<list_of_ints> :: type_2.
typedef Exploder<list_of_ints> :: type_2 should_be_an_allocator_int;
Given this second type, which we know was created by a template, we can access its parameter type, int, with Exploder< should_be_an_allocator_int > :: type_1, but it's more interesting to actually access the allocator template instead and pass a different parameter to it. This next line evaluates, in this example, to an allocator<double>.
typedef Exploder< should_be_an_allocator_int >
:: rebind<double> :: type should_be_an_allocator_double;
So, even if your list<...,...> type did not use the default allocator, you can access the allocator that was used, and also any class template that was used to create the allocator type.
Now that we have a suitable allocator, we can go back to our original template class list<int> and replace int with double:
Exploder<list_of_ints> :: rebind<double, should_be_an_allocator_double> :: type
To verify all this has worked, the sample code uses typeid(...).name() to print the actual type of the various objects, along with the correct type that it should be. You can see that they match.
(Also, some templates are such that their parameters are not types, but other class templates, or even other template templates. It should be possible to extract all that, but I'm not going to look into that here.)
(One last interesting technical note. Some types, such as allocator, have something called rebind to allow this sort of access. But the technique used above works for all template classes, even those without their own rebind)
The full code for the template Exploder
See sample code I've put on ideone for a full demo.
template <class>
struct Exploder;
template<class T, template<class> class Template>
struct Exploder< Template<T> > {
static const char * description() { return " One-arg template. Arg 1 is a type "; }
typedef T type_1;
template <class V>
struct rebind {
typedef Template<V> type;
};
};
template<class T, class U, template<class,class> class Template>
struct Exploder< Template<T,U> > {
static const char * description() { return " Two-arg template. All args are types, as opposed to being (unapplied) templates. "; }
typedef T type_1;
typedef U type_2;
template <class V,class W>
struct rebind {
typedef Template<V,W> type;
};
};
template<class S, class T, class U, template<class,class,class> class Template>
struct Exploder< Template<S,T,U> > {
static const char * description() { return " Three-arg template. All args are types, as opposed to being (unapplied) templates. "; }
typedef S type_1;
typedef T type_2;
typedef U type_3;
};
The second template parameter of the standard containers (the allocator) is a type, not a template, so you need to change your third parameter to something like
template<typename, typename> class C
(Note that the default arguments in your template parameter specifications don't serve any purpose, so I omitted them here.)
Then you should be able to instantiate the template as
queryable<int, std::allocator, std::vector>
You may be better off just parametrising over the container type, and then using its value_type and allocator_type definitions:
template <typename C> class queryable
{
public:
typedef typename C::value_type value_type;
typedef typename C::allocator_type allocator_type;
typedef C container_type;
};
(One downside is that you can't directly access the allocator template; however, you can use the allocator type's nested rebind definition if you need to instantiate that template for other types.)
Also, typedef iterator const const_iterator; is wrong; that declares an unmodifiable iterator that can be used to modify the sequence, while const_iterator is supposed to be a modifiable iterator that can't be used to modify the sequence.
(A note on terminology. vector is a class template, i.e. without any parameters. And vector<int> is a template class, i.e. a class almost like any other except that it was created by a template.)
It is possible to use it as you wish, where queryable takes one template parameter:
queryable< vector<int> > q;
This means that querable is a template with just one parameter:
template <typename T>
struct queryable;
You then use a specialization that has more than one parameter:
template <typename ValueType, template<class T,class = allocator<T> > class ContainerTemplate>
struct queryable< ContainerTemplate<Contained> > {
typedef ValueType value_type;
typedef ContainerTemplate<ValueType> container_type;
typedef typename container_type :: allocator_type A;
// typedef ContainerTemplate <WhateverOtherValueTypeYouWish> ...
// typedef A :: rebind <SomeOtherType> ...
};
The last two lines, commented out, show how you can use ContainerTemplate as a class template, creating other types as required. ContainerTemplate is vector or list or set or something like that.
As #MikeSeymour has pointed out, the use of rebind might be the way to access the allocator class template.
Sample code on ideone

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;