C++ typedef and templates syntax? - templates

I was reading this tutorial on variadic templates, but in below code:
template<int index, class C>
struct container_index {
// points to the "next" container type
typedef typename container_index<
index-1,
typename C::base_container
>::container_type container_type;
// points to the next T-type
typedef typename container_index<
index-1,
typename C::base_container
>::type type;
};
these typedefs seems redundant but it compiles well. The problem is simply I dont understand why they are like this and I didnt find a tutorial explaining this case. Could someone give some explanation? Why the typedef name is repeated:
"::container_type container_type;"
"::type type;"
It couldn't be just like that:
typedef typename container_index<
index-1,
typename C::base_container
> type;
Many thanks.

The example demonstrates a recursive type definition in templates. The key is that the recursion base case is specified as a specialisation for index=0:
template<class C>
struct container_index<0, C> {
// point to C instead of C::base_container
typedef C container_type;
// point to C::type instead of C::base_container::type
typedef typename C::type type;
};
It is this base-case that makes the type deduction possible. So for example, the type container_index<2, MyCont>::container_type is expanded to container_index<1, MyCont>::container_type, which in turn expands to container_index<0, MyCont>::container_type, which finally expands to MyCont.

typedef gives a type a name. So you need to supply both the type and the name you want to give it.
In
typedef typename container_index<index-1, typename C::base_container>::type type;
the typename container_index<index-1, typename C::base_container>::type is us describing the type we want to give a name to, and that final type before the semicolon is the name we want to call it.
Compare:
struct Example
{
typedef Fruit::orange citrus; // declare a type called Example::citrus, which is the same type as Fruit::orange
typedef Fruit::apple apple; // declare a type called Example::apple, which is the same type as Fruit::apple - the same operation as the line above, and so the same syntax!
};

Related

Adding typename causes program to fail compilation

So I have this code:
#include "type_traits"
struct A{
int member;
};
struct B{
typedef A object;
typedef int member;
};
typedef std::integral_constant<B::member, B::object::*, &A::member> type;
But if I change the final line to:
typedef std::integral_constant<typename B::member, typename B::object::*, &A::member> type;
The program will not compile....
Why does adding the typename specifier cause the program to not compile? This is especially surprising to me because I thought I needed it in this case.
Note:
Using gcc 5.1.0
You cannot add typename everywhere you want to specify a type. You can only, and require to add typename when you use a dependent type name.
A dependent name is something like this:
template<typename T>
void foo() { (void)T::member(); }
Is T::member a type, or a member function named member? The compiler will assume it's not a type by default. If it is a type, you must specify typename to disambiguate.
template<typename T>
void foo() { (void)typename T::member(); }
Now the compiler is told to assume that T::member is indeed a type.
However, the C++ syntax only allow it in cases that the nature of T::member cannot be known. So when dealing with know types, like your code, the compiler already know that these members are types. There's nothing to desambiguate.
If you were to change you typedef by a template alias, it would require typename as you wrote:
template<typename C, typename D> // v----- Type of pointer to member?
using type = std::integral_constant<typename D::member D::object::*, &C::member>;
// Here, D::object::* don't need typename, ----^
// since only types are allowed here

what does the < > beside the struct do?

Ok the following code is copied from another stackoverflow question
here
template<typename T>
struct remove_pointer
{
typedef T type;
};
template<typename T>
struct remove_pointer<T*>
{
typedef typename remove_pointer<T>::type type;
};
While I do understand that this is a recursive definition in template, what puzzled me are the lines
template<typename T>
struct remove_pointer<T*>
does that mean remove_pointer will result in T=int*? Why wouldn't T=int**? explanation is appreciated.
This is a specialization for pointer types. Specializations may also have template parameters. So this template's type is just T in the general case but, if T is a pointer type, then its type is T with the pointer removed. Maybe it is more clear when giving the parameter a different name:
template<typename T>
struct remove_pointer
{
typedef T type;
};
template<typename S>
struct remove_pointer<S*> // specialization for T = S*
{
typedef typename remove_pointer<S>::type type;
};
i.e. in the general case type is just T, but if T is a pointer, then the template is instantiated for S, where T == S*.
PS: I think the special thing about this example is that the specialization introduces a new template parameter. A "normal" specialization would look like this:
template<>
struct remove_pointer<int*> // specialization for T = int*
{
typedef typename remove_pointer<int>::type type;
};
However, this isnt very useful, as we want it to work for any type. The solution is to introduce an additional template parameter on the specialization (S). Afaik this additional parameter has to be deducible from the parameters to the original template, in this case S can be deduced, because S is just T without the pointer.

Pointer to member of a typename

Consider this:
template < typename VectorType >
void ff()
{
// This passes.
typedef typename VectorType::value_type VV;
typedef int VV::* MM;
// This FAILS!??
typedef int typename VectorType::value_type::* MMM;
}
Why the second fails and what is the correct way to get the desired typedef in one typedef statement?
My compiler is the GCC-4.7.2.
As pointed out in the comments, you have a typename where it shouldn't be:
typedef int typename VectorType::value_type::* MMM;
should be just:
typedef int VectorType::value_type::* MMM;
typename is used when you have a::b inside a template, a depends on template parameters and b is a type. In that case, you have to use typename a::b to communicate this fact to the compiler.
On the other hand, you're doing a::b::*, which is a clear indicator that b must be a type, so typename cannot be used here.

C++11 "late binding" of template arguments

Please don't get my "late binding" wrong, I don't mean usual late binding at runtime, I mean something else and cannot find a better word for it:
Consider I am working on a container (or similar) data structure Containor for some value type V that needs to compare these values with a comparator, so my first template looks like this
template<typename Val, typename Comp = std::less<Val>>
struct Containor{};
Now, my Containor structure makes use of another container internally. Which container is to be used should be configurable by template arguments as well, lets say the default is std::set. So my next version of Containor looks like this:
template<typename Val, typename Comp = std::less<Val>, typename Cont = std::set<Val,Comp>>
struct Containor{};
and here is where the code begins smelling IMHO. As long as the user is satisfied with the default implementation of the inner container, everything is fine. However, suppose he wants to use the new google btree set implementation btree::btree_set instead of std::set. Then he has to instanciate the template like this:
typedef Containor<int,std::less<int>,btree::btree_set<int,std::less<int>> MyContainor;
^^^^^^^^^^^^^^^^^^^
I have underlined the part where my problem lies. The CLIENT CODE has to instanciate the btree_set with the right parameters. This honestly sucks, because the Containor class always needs a set of exactly the same type and comparator as its own first two template arguments. The client can - by accident - insert other types here! In addition, the client has the burden of choosing the right parameters. This might be easy in this case, but it hard if the inner container must for example be a set of pairs of the value type and some other type. Then the client has an even harder time getting the type parameters of the inner set correct.
So what I want is a way in which the client code only hands in the raw template and the Containor internally instanciates it with the correct arguments, i.e. something like that:
template<typename Val, typename Comp = std::less<Val>, typename Cont = std::set >
struct Containor{
typedef Cont<Val,Comp> innerSet;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ container instanciates the inner containor
};
typedef Containor<int,std::less<int>,btree::btree_set> MyContainor;
// ^^^^^^^^^^^^^^^^
// client only hands in raw template
Of course, this is no valid C++!
So I thought about ways to solve this problem. The only solution I could think of was writing "binder classes" for all data structures I want to use, like this:
struct btree_set_binder{
template<typename V, typename C = std::less<V>>
struct bind{
typedef btree::btree_set<V,C> type;
}
};
Now I can define my Containor with a set binder
template<typename Val, typename Comp = std::less<Val>, typename ContBinder = btree_set_binder >
struct Containor{
typedef btree_set_binder::bind<Val,Comp>::type innerSet;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ works like a charm
};
Now, the user must only supply the desired binder class and the Containor will instanciate it with the right arguments. So these binder classes would be okay for me, but it is quite a hassle writing binder classes for all containers. So is there a better or easier way to bind template arguments "late" in C++11, i.e., inside another template that retrieves the raw template as parameter.
Maybe make your own comparator trait.
// Comparator trait primary template
template <typename T> stuct MyComparator
{
typedef typename T::key_compare type;
};
// Comparator trait non-standard usage example
template <typename U, typename V, int N>
struct MyComparator<WeirdContainer<U, V, N>>
{
typedef std::greater<V> type;
};
template <typename T, typename Cont = std::set<T>>
struct MyAdaptor
{
typedef typename MyComparator<Cont>::type comparator_type;
typedef T value_type;
// ...
};
I've renamed your "Containor" to "MyAdaptor", since this sort of construction is usually called an "adaptor" class.
Usage:
MyAdaptor<int> a; // uses std::set<int> and std::less<int>
MyAdaptor<double, WeirdContainer<bool, double, 27>> b;
Update: In light of the discussion, you could even remove the outer type argument entirely:
template <typename Cont> struct MyBetterAdaptor
{
typedef MyAdaptorTraits<Cont>::value_type value_type;
typedef MyAdaptorTraits<Cont>::pred_type pred_type;
// ...
};
To be used like this:
MyBetterAdaptor<std::set<int>> c; // value type "int", predicate "std::less<int>"
Writing the MyAdaptorTraits template is left as an exercise.
So what I want is a way in which the client code only hands in the raw
template and the Containor internally instanciates it with the correct
arguments,
So what you need is obviously a template template parameter:
// std::set has three template parameters,
// we only want to expose two of them ...
template <typename V, typename C>
using set_with_defalloc = std::set<V,C>;
template<
typename Val,
typename Comp = std::less<Val>,
template <typename V, typename C> class Cont = set_with_defalloc>
struct Containor{
typedef Cont<Val,Comp> innerSet;
// ...
};
You should be able to do it with template template parameters, as in:
template<typename Val, typename Comp = std::less<Val>, template <typename...> class ContBinder = std::set>
struct Containor {
typedef ContBinder<Val, Comp> innerSet;
// ...
};
Note: you need the variadic typename... because std::set takes three template parameters (the third being an allocator) while other containers might not.

How to typedef a type derived through several layers of templates?

Maybe my Google-fu just isn't strong enough.
Using GCC 4.4.3, I've got a set of classes like this:
template <typename storage_t, typename index_t = std::size_t, typename
leaf_payload_t = std::size_t>
struct btree_node {
public:
typedef btree_node<storage_t, index_t, leaf_payload_t> this_t;
typedef boost::shared_ptr<this_t> handle_t;
// [...]
};
template <typename storage_t, typename index_t = std::size_t, typename
leaf_payload_t = std::size_t>
class btree {
public:
class caching_storage_t;
typedef btree_node<caching_storage_t, index_t, leaf_payload_t> node_t;
typedef typename node_t::handle_t nodehandle_t;
// [...]
class caching_storage_t {
public:
//typedef typename btree::nodehandle_t nodehandle_t; // Fails -- why?
typedef typename boost::shared_ptr<node_t> nodehandle_t;
// [...]
};
};
As you can see, I've had to redefine nodehandle_t in caching_storage_t, because if I try it with the commented-out typedef line (which I'd prefer), I get an error "no type named ‘handle_t’ in ‘struct btree_node<...>’" -- which is obviously incorrect, and the compiler knows it since the typedef works fine in btree. I've also tried a using typename btree::nodehandle_t;, and every variation I've been able to think of on both, to no avail.
Is this a language/syntax problem (and if so, what is the right syntax), or is it a compiler bug?
(There's a similar question here, but it doesn't seem to apply because the thing I'm trying to typedef isn't itself a template. Nothing else I've been able to find seems even close.)
It looks like a compiler bug to me. To be sure, I put your code into clang and instantiated btree<int>::caching_storage_t, all working fine also with the comment chars removed.
It also works on GCC4.5.1 and GCC4.3.4.