Why can't I use alias from a base class in a derived class with templates?
Asked Answered
C

3

7

Consider this C++ code :

template<typename Session>
class Step
{
public:
   using Session_ptr = boost::shared_ptr<Session>;
protected:
   Session_ptr m_session;
public:
   inline Step(Session_ptr session) : 
      m_session(session)
   {}

};

template<typename Socket>
class Session
{
public:
   Socket a;

   Session(Socket _a):
      a(_a)
   {}
};

template <typename Socket>
class StartSession : public Step<Session<Socket> >
{
protected:
   Session_ptr m_session; //Unknown type Session_ptr
public:
   inline StartSession(Session_ptr session) :
      Step<Session<Socket> >(session)
   {}

   void operator()(const boost::system::error_code& ec);
};

template <typename Socket>
class StartSession2 : public Step<Session<Socket> >
{
protected:
   typename Step<Session<Socket> >::Session_ptr m_session;
public:
   inline StartSession2(typename Step<Session<Socket> >::Session_ptr session) :
      Step<Session<Socket> >(session)
   {}

   void operator()(const boost::system::error_code& ec);
};

int main(int argc, char * argv[])
{
   Step<Session<int> >::Session_ptr b(new Session<int>(5)); //no problem
   StartSession<int >::Session_ptr bb(new Session<int>(5)); //gcc ok, clang refuses to remember the symbol since the class has errors
   StartSession2<int >::Session_ptr bbb(new Session<int>(5)); //no problem
   std::cout << b->a; // ok
   std::cout << bb->a; // gcc ok, clang bb not declared
   std::cout << bbb->a; // ok
   return 0;
}

As you can see, there are some strange (to me at least) things happening here...

First, why isn't Session_ptr accessible in the child classes ? I know because these are templated class, that make things more complicated... But I don't see any ambiguity here that makes the use of typename mandatory...

Then, why in the main, Session_ptr is accessible either as member of the base class either as member of a child class ?

Cultivation answered 5/9, 2016 at 15:59 Comment(0)
P
11

Unqualified lookup does not look in dependent base classes in class templates.

So here:

template <typename Socket>
class StartSession : public Step<Session<Socket> >
{
protected:
   Session_ptr m_session; // <== unqualified name lookup on Session_ptr
   // ...
};

Step<Session<Socket>> is a dependent base class of StartSession<Socket>. In order to lookup there, you'll have to do qualified name lookup (which is what you're doing in StartSession2):

template <typename Socket>
class StartSession : public Step<Session<Socket> >
{
protected:
   typename Step<Session<Socket>>::Session_ptr m_session;
   // ...
};

Or simply add the alias yourself:

using Session_ptr = typename Step<Session<Socket>>::Session_ptr;
Pollitt answered 5/9, 2016 at 16:4 Comment(3)
Can't you just use using typename Step<Session<Socket>>::Session_ptr?Agglutination
@Agglutination Yes, you can do that too.Pollitt
Less well known is that you can do the qualified lookup using the derived class (template) name, which avoids repeating the template arguments for the base: typename StartSession::Session_ptr.Brackely
C
3

This is because in StartSession class your Session_ptr type is seen as non dependent name, so it is being looked up with out instatiation of base class which is dependent. This is why you need to make reference to this name dependent some how, for example by qualifying it in a way as g++ suggests in warnings:

note: (perhaps 'typename Step<Session<Socket> >::Session_ptr' was intended)

btw. some compilers like Visual Studio (I checked it with 2015 now) will happily compile your code. This is because VS does not implement properly a two-phase template instantiation. See here for more on that: What exactly is "broken" with Microsoft Visual C++'s two-phase template instantiation?

Cai answered 5/9, 2016 at 16:10 Comment(0)
G
0

Here I give another example with the solutions already given in other answers presented.

std::iterator<std::input_iterator_tag, MyType> is a dependent base class of Myterator<MyType>. In order to lookup there, you'll have to do qualified name lookup.

// std::iterator example  from http://www.cplusplus.com/reference/iterator/iterator/
//***************************************************************************************
#include <iostream>     // std::cout
#include <iterator>     // std::iterator, std::input_iterator_tag

template <class MyType>
class MyIterator : public std::iterator<std::input_iterator_tag, MyType>
{
  // The working alternatives, one per row
  //typename std::iterator<std::input_iterator_tag, MyType>::pointer  p;
  //using pointer = typename std::iterator<std::input_iterator_tag, MyType>::pointer; pointer p;
  //using typename std::iterator<std::input_iterator_tag, MyType>::pointer; pointer p;
  //using Iter = typename std::iterator<std::input_iterator_tag, MyType>; typename Iter::pointer p;
  pointer p; // This does not work while any of alternatives in comments above do work
public:
  MyIterator(MyType* x) :p(x) {}
  MyType& operator*() {return *p;}

};

int main () {
  int numbers[]={10,20,30,40,50};
  MyIterator<int> from(numbers);
  std::cout << *from << ' ';
  std::cout << '\n';

  return 0;
}
Globuliferous answered 16/4, 2020 at 15:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.