提问人:Šimon Hrabec 提问时间:10/23/2023 更新时间:10/23/2023 访问量:81
模板类的嵌套类 - 互操作性和可见性
Nested class of a template class - interoperability and visibility
问:
我有一个带有嵌套类的模板类 - 我用它来隐藏实现细节。我可能有不同的类实例化。 可以保存指针,我可能需要在 2 个不同的实例化中使用这些指针进行操作。在这些情况下,我可能会得到 2 个不同的 Inner 类 - 和 .从编译器的角度来看,它们是不同的类型,并且不兼容。一个非工作的例子:A<T>
Inner
A
A
Inner
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;
}
解决这个问题的一种方法可能是使用 ,但这很丑陋,我猜也是 UB。reinterpret_cast<Inner*>(other.inner_pointer)
另一种方法是不创建嵌套类,而是创建一个单独的类: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>
首先,声明(而不是定义)模板,并将 with 定义为嵌套类型。更一般地说,您可以将所有常用功能(如 inner_pointer
成员)移动到此专用化。这开启了将公共代码(包括 Internal
的定义)从头文件移出到源文件的可能性。A<void>
Inner
#include <cstdint>
// Declare the template.
template<typename T>
class A;
// Define A<void>
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
评论
detail
impl
detail
命名空间通常用于的用途A<T>::Inner
A<U>::Inner