I have a template class A<T> with a nested class Inner - I use it to hide implementation details. I might have different instantiations of the class A. A can hold a pointer to Inner and I might need to do operations with those pointers within 2 different instantiations. In those cases, I might get 2 different Inner classes - A<T1>::Inner and A<T2>::Inner. From a compiler perspective, these are different types and are not compatible. A non-working example:
template<typename T>
class A {
template <typename T2> friend class A;
struct Inner {
Inner(int64_t timestamp) : timestamp(timestamp) {}
int64_t timestamp;
};
Inner* inner_pointer;
T val;
public:
template<typename U>
A(const A<U> other, const T& val) : inner_pointer(other.inner_pointer), val(val) {}
A(const T& val, int64_t time_now) : inner_pointer(new Inner(time_now)), val(val) {}
};
int main(int argc, char const *argv[]){
A first('a', 123);
A second(first, 0.2);
return 0;
}
One way to get around this could be to use reinterpret_cast<Inner*>(other.inner_pointer), but that's ugly, and I would guess a UB as well.
Another way is not to make Inner and nested class, but a separate class instead:
struct Inner {
Inner(int64_t timestamp) : timestamp(timestamp) {}
int64_t timestamp;
};
template<typename T>
class A {
template <typename T2> friend class A;
Inner* inner_pointer;
T val;
public:
template<typename U>
A(const A<U> other, const T& val) : inner_pointer(other.inner_pointer), val(val) {}
A(const T& val, int64_t time_now) : inner_pointer(new Inner(time_now)), val(val) {}
};
This solves the problem, but then it makes the Inner class visible to anyone who uses the A class since it is part of the header file that needs to be included and it also pollutes the scope.
I guess the STL implementations get around this using names that are reserved (starting with underscore). Is there a simple/elegant way how to achieve this without exposing the Inner class?
Here's a trick based on the assumption that
A<void>is not a valid instantiation. (If this is an artifact of simplification, there are other ways to get a similar effect.)First, declare (not define) your template and define
A<void>withInneras a nested type. More generally, you could move all the common functionality, such as theinner_pointermember, to this specialization. This opens the possibility of moving the common code, including the definition ofInner, out of the header to a source file.Next, define your general template, using
A<void>as a base type.All your template instantiations now use the same
Innertype.The drawback of this is that we have not really changed the problem; the
A<void>type is visible to anyone who uses theAtemplate. However, it is not usable (since everything is private), and we have eliminated the namespace pollution as there is not a new identifier introduced.