为什么不能在派生类中使用基类中的别名和模板?



考虑下面的c++代码:

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;
}

正如你所看到的,这里发生了一些奇怪的事情(至少对我来说)…

首先,为什么Session_ptr不能在子类中访问?我知道,因为这些是模板化类,使事情变得更加复杂…但是我没有看到这里有任何歧义使得强制使用typename

那么,为什么在main中,Session_ptr既可以作为基类的成员也可以作为子类的成员访问呢?

不限定查找不会查找类模板中的依赖基类。

这里:

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

Step<Session<Socket>>StartSession<Socket>依赖基类。为了在那里查找,您必须执行限定名称查找(这就是您在StartSession2中所做的):

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

或者干脆自己添加别名:

using Session_ptr = typename Step<Session<Socket>>::Session_ptr;

这是因为在StartSession类中,您的Session_ptr类型被视为非依赖名称,因此它正在与依赖的基类的安装一起查找。这就是为什么您需要以某种方式依赖于此名称的原因,例如,通过像g++在警告中建议的那样对其进行限定:

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

顺便说一句。像Visual Studio这样的编译器(我现在用2015年检查过了)会很高兴地编译你的代码。这是因为VS没有正确地实现两阶段模板实例化。更多信息请看这里:"坏了"到底是什么?与Microsoft Visual c++ 's两阶段模板实例化?

这里我再举一个例子,在其他答案中已经给出了解决方案。

std::iterator<std::input_iterator_tag, MyType>Myterator<MyType>的依赖基类。为了在那里查找,您必须执行限定名称查找。

// 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;
}

最新更新