Рассмотрим этот код 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
доступен либо как член базового класса, либо как член дочернего класса?
Неквалифицированный поиск не ищет зависимых базовых классов в шаблонах классов.
Так вот:
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 ++ в два этапа создания шаблона?