Why is typename not needed always?

Hi,

I have this doubt: why is typename required in some cases and not in all?

For example, it is required in code like:

1
2
3
4
5
template<int Index, typename Head, typename...Tail>
struct TypeOfIndex<Index, Lista<Head, Tail...>>
{
	typedef typename TypeOfIndex<Index - 1, Lista<Tail...>>::type type;
};


but not in code using this template, like so:

1
2
3
using lista = Lista<int, double, char>;

typedef TypeOfIndex<0, lista>::type first_type;



Thanks!

Juan Dent
The typename disambiguator is required only if the name is a dependent name
(and for a class template, only if it is not the name of a member of the current instantiation).
See: http://en.cppreference.com/w/cpp/language/dependent_name

The 'Down with typename!' proposal: http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0634r1.html
One doubt I still have - in the code:

1
2
using lista = Lista<int,double,char>;
typedef TypeOfIndex<0, lista>::type first_type;


Why is type not a dependent name? type still depends on the template parameters given (namely <0,lista>)??

Thanks and sorry if its a silly question...

Juan
> type still depends on the template parameters given (namely <0,lista>)

In TypeOfIndex<0,lista>, 0 and lista are template-arguments.
Template-parameters appear only in the declaration or definition of a templated entity.
so in an explicit instantiation of a template, there is enough information to deduce that in:

 
typedef TypeOfIndex<0,lista>::type first_type;


the nested 'type' IS A typename, so no need to write typename. Correct?

Last edited on
You appear to be confused about the clear distinction a parameter and an argument.

1
2
3
4
5
6
7
8
int foo( int number )  ; // number is the parameter
const int x = foo(33) ; // 33 is the argument 
int foo( int number = 5 ) ; // number is the parameter, 5 is the default argument

template < typename T >  struct bar{ /* ... */ }  ; // T is the template-parameter
bar<int> bb ; // int is the template-argument

template < typename T = double >  struct bar ; // T is the template-parameter, double is the default template-argument 


The typename disambiguator is required only if the name is a dependent name,
(and for a class template, only if it is not the name of a member of the current instantiation).
A dependent name is a name for which the semantics depends on the template-parameters; that is, its meaning may differ between different instantiations of the template.
Template-parameters appear only in the declaration or definition of a templated entity;
ergo dependent names appear only inside a template.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <string>
#include <type_traits>
#include <vector>

template < typename T > // T is a template type parameter
struct foo
{
    // std::remove_reference<T>::type is a dependent name (depends on template parameter T)
    // typename disambiguator is required
    using value_type = typename std::remove_reference<T>::type ;

    // value_type is a dependent name (depends on template parameter T)
    // name of a member of the current instantiation: typename disambiguator is not required
    //
    // std::remove_cv<value_type>::type is a dependent name (depends on template parameter T)
    // typename disambiguator is required
    using unqualified_value_type = typename std::remove_cv<value_type>::type ;
    using iterator_type = typename std::vector<unqualified_value_type>::iterator ;

    // iterator_type is a dependent name (depends on template parameter T)
    // name of a member of the current instantiation: typename disambiguator is not required
    foo( iterator_type iter ) : iter(iter) {}
    iterator_type iter ;
};

template <> struct foo<int> // int is not a template-parameter; it is a template-argument
{
    using T = int ; // T is not a dependent name

    // none of the following involve dependent names
    // (there are no dependent names at all in this class: because there are no template-parameters)
    using value_type = std::remove_reference<T>::type ;
    using unqualified_value_type = std::remove_cv<value_type>::type ;
    using iterator_type = std::vector<unqualified_value_type>::iterator ;
    foo( iterator_type iter ) : iter(iter) {}
    iterator_type iter ;
};

int main()
{
    // instantiate foo with template-argument double
    // this is not the definition of a templated entity
    // there are no dependent names
    foo<double> my_foo( {} ) ;
    const decltype(my_foo)::unqualified_value_type v = 7 ;
    std::cout << std::fixed << v << '\n' ;
}

Couldn't be explained any better JLBorges!!

Much appreciated as always

Juan Dent
Topic archived. No new replies allowed.