【问题标题】:Why can't I use alias from a base class in a derived class with templates?为什么我不能在带有模板的派生类中使用来自基类的别名?
【发布时间】:2017-01-13 00:49:33
【问题描述】:

考虑一下这个 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 强制使用......

那么,为什么Session_ptr 可以作为基类的成员或作为子类的成员访问?

【问题讨论】:

    标签: c++ templates c++11 type-alias


    【解决方案1】:

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

    所以这里:

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

    Step&lt;Session&lt;Socket&gt;&gt;StartSession&lt;Socket&gt;依赖 基类。为了在那里查找,您必须进行 qualified 名称查找(这就是您在 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;
    

    【讨论】:

    • 你不能用using typename Step&lt;Session&lt;Socket&gt;&gt;::Session_ptr吗?
    • @plasmacel 是的,你也可以这样做。
    • 鲜为人知的是,您可以使用 派生 类(模板)名称进行限定查找,从而避免重复基类的模板参数:typename StartSession::Session_ptr
    【解决方案2】:

    这是因为在StartSession 类中,您的Session_ptr 类型被视为非依赖名称,因此在查找它时没有实例化依赖的基类。这就是为什么您需要以某种方式引用此名称的原因,例如通过 g++ 在警告中建议的方式对其进行限定:

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

    顺便说一句。一些编译器,如 Visual Studio(我现在用 2015 进行了检查)会很高兴地编译你的代码。这是因为 VS 没有正确实现两阶段模板实例化。有关更多信息,请参见此处:What exactly is "broken" with Microsoft Visual C++'s two-phase template instantiation?

    【讨论】:

      【解决方案3】:

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

      std::iterator&lt;std::input_iterator_tag, MyType&gt;Myterator&lt;MyType&gt; 的依赖基类。为了在那里查找,您必须进行限定名称查找。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-11-15
        • 1970-01-01
        • 2015-09-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-03
        相关资源
        最近更新 更多