8

I have code that compiles fine with VC9 (Microsoft Visual C++ 2008 SP1) but not with GCC 4.2 (on Mac, if that matters). If I pile on enough qualifiers and keywords I can force it to work in GCC but this doesn't seem right.

Here's a minimal code sample exhibiting my problems:

template< typename N >
struct B {
    typedef N n_type;                     // can derived class access typedef?
    void foo() {}                         // can derived class access function?
};

template< typename N >
struct D : public B<N> {

    typedef B<N> b_type;
    typedef typename b_type::n_type bn_type;

    void f1( n_type ) {}                  // ERROR: 'n_type' has not been
                                          // declared

    void f2( typename B<N>::n_type ) {}   // OK, verbose

    void f3( b_type::n_type ) {}          // ERROR: 'struct B<N>::n_type' is 
                                          // not a type

    void f4( typename b_type::n_type ) {} // OK, verbose

    void f5( bn_type ) {}                 // OK, verbose typedefs

    void f6() { foo(); }                  // ERROR: there are no arguments to
                                          // 'foo' that depend on a template
                                          // parameter, so a declaration of
                                          // 'foo' must be available

    void f7() { b_type::foo(); }          // OK, verbose

};

Am I wrong to expect a template class derived from another template class to be able to use inherited typedefs and functions directly? Is there a better way to do this than what I've come up with so far?

jwfearn
  • 28,781
  • 28
  • 95
  • 122
  • What's the point of `typedef N n_type`? you can just use `N` directly – Trent Jan 19 '10 at 16:58
  • 3
    typedef is not inherited, Visual C++ does not comply with standards often. inherited functions can be accessed using scope resolution, IE base::f or this->f. you can also use "using base::f" to bring function into local scope. – Anycorn Jan 19 '10 at 17:06
  • It works for me except for the case of f3. – Alexey Malistov Jan 19 '10 at 17:14
  • @Trent: In generic code that accepts any container, you can use `C::value_type` without knowing if it's `std::vector` or `std::deque`. In code that does `using myB=B;`, you can use `myB::n_type` to connect them without an extra `typedef` for the template argument. – Davis Herring Jan 11 '18 at 14:26

4 Answers4

9

Am I wrong to expect a template class derived from another template class to be able to use inherited typedefs and functions directly?

Yes, this will not generally work as you expect it. The C++ name lookup rules specify that a name is only searched in a templated base classes if it depends on a template parameter (if it is a "dependent name"). If a name does not depend on a template parameter it isn't searched there. (Also see this C++ FAQ Lite entry)

To call functions from a dependent base class the most easy way is to use this->, since this is always implicitly a dependent name:

void f6() { this->foo(); }
sth
  • 222,467
  • 53
  • 283
  • 367
  • 1
    `this` doesn't help with `n_type`, but oh well, that's just how it is. – ephemient Jan 19 '10 at 17:21
  • 1
    You could add `typedef typename B::n_type n_type;` to get a type `n_type` that refers to the correct thing, but maybe it's better to be explicit there. – sth Jan 19 '10 at 17:32
3

See the C++ FAQ Lite § 35.18-20 and the C++ Templates FAQ.

Two-phase name lookup is a tricky part of C++ that many compilers (and coders) get wrong. Suffice it to say, GCC is being more correct (as per the C++ specification) than MSVC is here, and no, there is no better way to do what you want.

ephemient
  • 198,619
  • 38
  • 280
  • 391
2

There is no type n_type. There is (or may be) a type with that name dependent on N. So no, you can't do what you want.

1

Here are a few links that could help understand why it needs a typename keywords to be used:

  1. Stan Lippman's blog
  2. StackOverflow.com
  3. comeaucomputing

Seems that GCC is 'doing it right'

Community
  • 1
  • 1
Dmitry
  • 6,590
  • 2
  • 26
  • 19