c++ 是否将嵌套类用作父类模板参数?

lvmkulzt  于 2023-01-15  发布在  其他
关注(0)|答案(3)|浏览(119)

我有一个嵌套的类定义,我想把它作为父类传递下去(* 包含嵌套类的类,而不是被继承的类 *)模板参数。由于模板似乎不知道嵌套类的存在,我试图将其作为不完整类型传递下去,只是在后面读到,这样做通常是个坏主意,并且很少被允许(例如在shared_ptr的情况下)。
我知道这可以很容易地通过简单地在外部声明嵌套类来解决(这就是我所做的),但我问这个问题是因为我想了解是否有任何方法可以达到同样的效果,因为我喜欢它们如何不污染命名空间,如何与父类定义“关联”而不暴露任何内容。
这里有一个例子,也许可以更清楚地说明我在说什么:

#include <memory>

using namespace std;

template <class T> class shared_class {
protected:
  shared_ptr<T> d = make_shared<T>();

  T* container() { return d.get(); }
};

                                   // A is the "parent" class.
class A : public shared_class<C> { // Compiler error: C is undefined. 
                                   // Similarly, A::C will complain that no C is in A.
  class C {                        // Nested class
  public:
    int i = 0;
  };
};

有没有另一种方法可以用模板来实现这一点,即嵌套类的整个定义完全包含在父类定义中?

zengzsys

zengzsys1#

简而言之:不,你不能这样做。在SO上你可以找到一些类似的问题,但这都归结为能够向前声明嵌套类,没有定义你就不能这样做。例如,见this question
换句话说,你可能会认为,只要你挥挥手,你就可以做这样的事情:

class A;
class A::C; // Error: not real C++

class A : public shared_class<A::C> { // Error: incomplete base class
  class C {
  public:
    int i = 0;
  };
};

但是你不能这样做,你需要分离C的声明,如果它要被用作基类,它需要一个定义,参见this之类的问题。
如果您最关心的只是名称空间封装,那么您总是可以使用专用的namespace,这就是它们的用途。

lf5gs5x2

lf5gs5x22#

据我所知,对于你所提出的要求,你的具体情况没有解决办法。
直接使用C作为模板参数在任何情况下都不能工作,原因很简单,因为此时编译器还没有看到C被声明为A的成员类,而且在此之前没有办法声明嵌套类。
它在您的特定情况下不起作用,但有一些限制,您可以通过traits模板或直接通过别名声明向shared_class模板参数的类型添加一个间接寻址,这将允许您延迟确定T是否应该是C,直到定义了A之后,并将C保持为嵌套类。
不幸的是,如果你想在一个声明中使用shared_class的类型T,这个声明将使用类模板特化来示例化,这里是shared_ptr<T> dT* container(),后者可以通过声明返回类型auto来保存,但是前者不能。
我们的想法是让shared_class在任何地方都使用typename T::shared_class_type,而不是T
A将定义为

class A : public shared_class<A> {
public:     
  class C {                        
  public:
    int i = 0;
  };
  using shared_class_type = C;
};

或者,您可以定义一个template<typename T> struct shared_class_traits;,它可能包含一个默认实现,该默认实现可能在使用using shared_class_type = A::C;成员定义A之后专门用于A,以便shared_class可以在任何地方使用typename shared_class_traits<T>::shared_class_type。这比使用shared_class_type“保留”所有类的特定成员名称要灵活得多。
但是正如我上面解释的,只要shared_class在类模板特化示例化的上下文中使用shared_class_type,它就不会工作,所以在成员函数体或默认成员初始化器中就可以,但在数据成员或成员函数类型中就不行。

relj7zay

relj7zay3#

正如其他答案所指出的,这个问题没有直接的解决方案,因为没有办法向前声明类C。
为了仍然能够编写A::C,我的解决方案是:

namespace {
    class C_Anon{
    public:
        int i = 0;
    };
}

template <class T> class shared_class {
protected:
    shared_ptr<T> d = make_shared<T>();

    T* container() { return d.get(); }
};

// A is the "parent" class.
class A : public shared_class<C_Anon> {
    using C = C_Anon; //Allows for writing A::C
};

相关问题