考虑下面的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;
}