Consider a class A, STL container B and a binary predicate C for the container.
Container B is used in class A. But class A is also used in the binary predicate C.
struct C{
bool operator()(A const &a, A const &b){
return a.compare_variable < b.compare_variable;
}
};
We need this predicate in order to define the container, which uses it to order its elements.
Because the container declaration becomes rather long, I used typedef to simplify the declaration.
typedef B<A, vector<A>, C> type;
Finally, my goal is to declare the container B ---whose declaration is abbreviated to "type" --- inside the class A. Namely, as a static public member variable.
class A{
public:
type container1, container2;
};
What is the right order is which to declare A, B and C?
I tried the following variations in the order:
first declaring the class A, then the struct C and at last the typedef, I get the error that container1, container2 do not name a type --- type did not exist at the time of the class declaration;
first the typedef: loads of errors --- both the class and struct are not defined yet;
Is the method that I use unnecessarily cumbersome and does there exist a more elegant solution?
Important caveat : Standard library containers like std::vector
technically do not support incomplete types . Instantiating them with an incomplete type is undefined behavior. The type A
is incomplete in the definition of A
, which means that you can't reliably use, for example, a member of type std::vector<A>
in the definition of A
. Thus, you'd want to use something like one of Boost's containers that has guaranteed support for incomplete types.
The below discussion assumes that B
and vector
support instantiation with incomplete types. If they don't, it would be impossible to do what you are trying to do.
First, figure out the dependencies:
struct C {
bool operator()(A const &a, A const &b){
return a.compare_variable < b.compare_variable;
}
};
Defining C
itself, including declaring C::operator()
, only requires A
to be forward-declared. However, defining C::operator()
requires the full definition of A
, because the function body references a member of A
.
typedef B<A, vector<A>, C> type;
Defining type
merely requires A
, vector
, B
, and C
to be forward-declared. A typedef by itself doesn't trigger instantiation of the templates.
class A{
public:
type container1, container2;
};
This triggers the instantiation of B<A, vector<A>, C>
, requiring the full definition of B
. Containers are likely to also require C
, the comparator, to be a complete type, since they need to store a copy of it.
So, in short:
C
requires a forward declaration of A
. Defining C::operator()
requires the full definition of A
. type
requires the forward declaration of A
, B
and C
. A
requires the full definition of B
and C
. Once you've sorted out the dependencies, you can write the code. Assuming B
is defined by including the appropriate header:
class A; // Forward declare A for C's definition
struct C {
bool operator()(A const &a, A const &b);
};
typedef B<A, vector<A>, C> type;
class A{
public:
type container1, container2;
};
inline bool C::operator()(A const &a, A const &b){
return a.compare_variable < b.compare_variable;
}
Note that you need to actually make a compare_variable
member in A, obviously.
So you want something like this?
class A {
struct C{
bool operator()(A const &, A const &);
};
typedef B<A, vector<A>, C> type;
type c1, c2;
public:
int compare_variable; // ?
};
// the definition below should either go in a .cpp file,
// or you should mark it inline
bool A::C::operator()(A const &x, A const &y) {
return x.compare_variable < y.compare_variable;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.