问题
I imitated the std::enable_shared_from_this
to create a template class, but I made the class use the type definition in its subclass.
Unfortunately!
Although I used typename
, after compiling,
//
// https://ideone.com/eYCBHW http://ideone.com/eYCBHW
#include <iostream>
#include <set>
#include <map>
using namespace std;
template<class _S> struct A {
};
template<class _Subclass>
class Global {
public:
typedef typename _Subclass::connection_t connection_t;
//std::map<std::string, _Subclass::connection_t> connections;
//std::set<_Subclass::connection_t> connections;
//using typename _Subclass::connection_t;
//typename _Subclass::connection_t* connections;
//connection_t* connections;
};
class CConnection {};
class SConnection;
class Client : public Global<Client> {
public:
typedef CConnection connection_t;
};
#if 0
class Server : public Global<Server> {
public:
typedef SConnection connection_t;
};
#endif
class SConnection {};
int main() {
// your code goes here
return 0;
}
GCC complained:
prog.cpp: In instantiation of ‘class Global<Client>’:
prog.cpp:25:23: required from here
prog.cpp:14:43: error: invalid use of incomplete type ‘class Client’
typedef typename _Subclass::connection_t connection_t;
^~~~~~~~~~~~
prog.cpp:25:7: note: forward declaration of ‘class Client’
class Client : public Global<Client> {
^~~~~~
How to solve it?
References
- Where and why do I have to put the “template” and “typename” keywords?
- C++ - meaning of a statement combining typedef and typename [duplicate]
- Two template classes use each other as template argument
回答1:
Having a typedef
at class level requires the template arguments to be complete types. How would the compiler otherwise be able to check, if the type provided as argument actually has some equivalent typedef itself?
Analogously, the following is going to fail:
class C;
using G = Global<C>; // C is not a complete type!
class C // too late...
{
// ...
};
Problem with curiously recurring template pattern, which is what you're trying to implement, that at the point you try to derive, the class is not yet complete, just as in my example above:
class Client : public Global<Client> // client is not yet complete!
{
}; // only HERE, it will get complete, but that's too late for above
Ever wondered, though, why member variables are known within member functions even though being declared after the function? That's because
class C
{
void f() { n = 12; }
int n = 10;
};
is compiled as if it was written as:
class C
{
inline void f();
int n = 10;
};
void C::f() { n = 12; } // n is known now!
This is at the same time the clue where you can use the template argument the way you intend:
template<class T> // different name used! *)
class Global
{
public:
void f()
{
typedef typename T::connection_t connection_t; // possible here!
// (similar to why you can use the static cast as in the link provided)
}
};
That won't help, though, with your members:
std::map<std::string, typename T::connection_t> connections;
// ^ additionally was missing, but won't help either
T
still remains incomplete at this point.
Within the comments, though, you only seem to use the connection type. If you don't need the client or server class for any reason other than the typedef, you can solve the issue pretty simply:
template<class T> // different name used! *)
class Global
{
std::map<std::string, T> connections;
// ^ use T directly
};
class Client : public Global<CConnection>
// ^ make sure it is defined BEFORE
{
// ...
};
Otherwise, you need to fall back to other means, e. g. the pimpl pattern, where you would let the implementation class inherit from the template.
*) Identifiers starting with underscore followed by captial letter, as well as those containing two subsequent identifiers, are reserved for the implementation (i. e. for use by the compiler). Defining your own such ones yields undefined behaviour.
Edit (stolen from the comments):
If you need client or server from within Global
, you could provide both as separate template paramters as well:
template <typename Base, typename Connection>
{
// use Connection directly, e. g. for member definitions
// and Base within member functions as mandated by CRTP
};
class Client : public Global<Client, CConnection>
{ /* ... */ };
来源:https://stackoverflow.com/questions/57546714/extend-a-template-classe-using-the-type-definition-in-subclass