我有一个模板类
A<T>
和一个嵌套类Inner
- 我用它来隐藏实现细节。我可能有不同的类实例 A
。 A
可以保存指向 Inner
的指针,我可能需要在 2 个不同的实例中对这些指针进行操作。在这些情况下,我可能会得到 2 个不同的内部类 - A<T1>::Inner
和 A<T2>::Inner
。从编译器的角度来看,它们是不同的类型并且不兼容。一个无效的例子:
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;
}
解决这个问题的一种方法可能是使用
reinterpret_cast<Inner*>(other.inner_pointer)
,但这很丑陋,我猜也是 UB。
另一种方法是不创建
Inner
和嵌套类,而是创建一个单独的类:
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) {}
};
这解决了问题,但它使
Inner
类对使用 A
类的任何人都可见,因为它是需要包含的头文件的一部分,并且还会污染范围。
我猜 STL 实现使用保留的名称(以下划线开头)来解决这个问题。有没有一种简单/优雅的方法可以在不暴露
Inner
类的情况下实现此目的?
这是一个基于
A<void>
不是有效实例化假设的技巧。 (如果这是简化的产物,还有其他方法可以获得类似的效果。)
首先,声明(而不是定义)您的模板,并将
A<void>
和 Inner
定义为嵌套类型。
#include <cstdint>
template<typename T>
class A;
template<>
class A<void> {
template <typename T2> friend class A;
struct Inner {
Inner(int64_t timestamp) : timestamp(timestamp) {}
int64_t timestamp;
};
// Private constructor prevents the use outside the A template.
A() = default;
// Protected (or private) destructor allows safe polymorphism without virtual functions.
~A() = default;
};
接下来,使用
A<void>
作为基本类型定义通用模板。
template<typename T>
class A : private A<void> {
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) {}
};
所有模板实例现在都使用相同的
Inner
类型。
这样做的缺点是我们并没有真正改变问题;任何使用
A<void>
模板的人都可以看到 A
类型。然而,它不可用(因为一切都是私有的),并且我们已经消除了命名空间污染,因为没有引入新的标识符。