Topic: >>, typename and template


Author: Ben Strasser <mail@ben-strasser.net>
Date: Sun, 21 Mar 2010 11:59:30 CST
Raw View
Hello,

I'm having a hard time finding any reliable document describing how
exactly the vector<vector<int>> problem has been solved. My
understanding so far is, that if is actually is a bitwise operator or >
is the logical one then they must be enclosed in (). Is this correct?

If it is, why doesn't this also solve the typename T::X problem? With it
I mean the fact, that you have to sprinkle template Code all over with
typenames and templates so that the compiler can actually find a couple
of bugs before instantiation. For example:

template<class T>
void foo(){
    typename T::X x;
}

As far as I understand the problem has 2 roots. First of it is not
always possible to correctly parse typenames without semantic knowlege.
For example:

A::A<A::B<A::C, A::D<A::E> >::X

This can be parsed in 2 ways:

A::A<(A::B<A::C), A::D<A::E> >::X

or

A::A<A::B<A::C, (A::D<A::E)> >::X

using the typename/template syntax it becomes also clear what is meant:

typename A::template A<A::B<A::C, A::template D<typename A::E> >::X

or

typename A::template A<A::template B<typename A::C, A::D<A::E> >::X

The later is however a lot more verbose and is IMHO a lot harder to read.

If one requires that also < operators must be enclosed in () then this
problem goes away. This would be only consequent considering the >>-path
chosen.

The other problem comes from those situations where the syntax doesn't
dictate whenever a type or an instance is expected. The only known
examples to me are:

sizeof(T::X)
typeid(T::X)
A::B<T::X>

All of these have in common that types exist so that any interpretation
is possible.

Lets consider for a moment what the compiler can even diagnose for
errors before instantiation. As far as I can tell it can only check
whenever T::X is always used as type or always as instance. For this
sort of usage it is however irrelevant whenever the T::X in sizeof(T::X)
is a type or not.

For example:

template<class T>
void foo(){
    T::X a; // ok T::X must be a type
    siezof(T::X); // well ok, works with T::X being a type
}

template<class T>
void foo(){
    // don't know if T::X is a type but don't care as either way is ok
    siezof(T::X);
}

template<class T>
void foo(){
    int a = T::X; // ok T::X must be an instance
    siezof(T::X); // well ok, works with T::X being an instance
}

template<class T>
void foo(){
    T::X a; // ok T::X must be a type
    siezof(T::X); // well ok, works with T::X being a type
    int a = T::X; // error T::X is a type
}

I'd love to see some way of reducing the syntactic noise caused by
typename & template.

If you want to see an example of library that gets hard to use as soon
as you use templates look at Lemon (http://lemon.cs.elte.hu/)

Algorithm code looks most of the time like:

template<class Graph>
void flow(const Graph&g, const typename Graph::Node&s, const typename
Graph::Node&t){
    typename Graph::template NodeMap<typename Graph::Node>node_map(g);
}

This is a common usage scenario. The typename & template noise makes up
about 33% (in terms of characters) of the whole example and IMHO becomes
unreadable.

If it is not possible to get rid of typename & template would it be
possible to make at least the following valid?

typename std::vector<int>::iterator foo;

Let T be a typedef. The following code breaks

typename T::X a;

if I change the typedef from a template parameter to a concret type.

Thanks for your time,
Ben Strasser

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Potatoswatter <potswa@gmail.com>
Date: Mon, 29 Mar 2010 13:01:57 CST
Raw View
On Mar 21, 12:59 pm, Ben Strasser <m...@ben-strasser.net> wrote:
> Hello,
>
> I'm having a hard time finding any reliable document describing how
> exactly the vector<vector<int>> problem has been solved. My
> understanding so far is, that if is actually is a bitwise operator or >
> is the logical one then they must be enclosed in (). Is this correct?

No. You can mix > and >> without using templates. The parser needs to
be smart enough to know whether a < is preceded by a template or an
integral constant, or whether > (and in C++0x, >>) is placed somewhere
a template can end. Which is pretty smart, granted, but apparently the
implementations are now polished enough.

It would be cool to see some vexing parses involving default
arguments

> If it is, why doesn't this also solve the typename T::X problem? With it
> I mean the fact, that you have to sprinkle template Code all over with
> typenames and templates so that the compiler can actually find a couple
> of bugs before instantiation. For example:
>
> template<class T>
> void foo(){
>     typename T::X x;
>
> }
>
> As far as I understand the problem has 2 roots. First of it is not
> always possible to correctly parse typenames without semantic knowlege.
> For example:
>
> A::A<A::B<A::C, A::D<A::E> >::X
>
> This can be parsed in 2 ways:
>
> A::A<(A::B<A::C), A::D<A::E> >::X
>
> or
>
> A::A<A::B<A::C, (A::D<A::E)> >::X
>
> using the typename/template syntax it becomes also clear what is meant:
>
> typename A::template A<A::B<A::C, A::template D<typename A::E> >::X

Typename is not an optional hint to the parser. It is necessary if
you're naming a type dependent directly or indirectly on a template
parameter to the current scope. Otherwise using it is an error. For
example, there is no "typename" at global scope.

The key is that either semantic knowledge is available, or typename is
required. Comeau accepts your example if I give it a context matching
the first parse:

struct A {
template< int b, class c >
struct A2 { enum { X = 5 }; };
enum { B = 1 };
enum { C = 2 };
template< class d >
struct D {};
struct E {};
};

enum { q = A::A2<A::B<A::C, A::D<A::E> >::X };

void f() {
int a[q], (&b)[5] = a;
}

or the second parse:

struct A {
template< class a >
struct A2 { enum { X = 5 }; };
template< class b, int c >
struct B {};
struct C {};
enum { D = 1 };
enum { E = 2 };
};


(I adjusted A::A which is illegal to A::A2.)

In fact, my first attempt at the second parse left the argument to A2
as an int, and Comeau specifically asked for a type instead!

I suppose the issue would be simpler of the parser simply ignored the
brace-enclosed scope following a template argument list, until the
template were instantiated, giving it full contextual knowledge. But
some degree of pre-instantiation syntax checking is the C++ policy
instead.


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Potatoswatter <potswa@gmail.com>
Date: Mon, 29 Mar 2010 13:02:37 CST
Raw View
Correction: typename isn't forbidden where it's not needed, such as
global scope. (   14.6/5) I thought I saw GCC complain about that, but
must be a false memory. "typename std::vector<int> x;" is legal at
global scope and accepted by GCC and Comeau.


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]