【问题标题】:Extend a template classe using the type definition in subclass使用子类中的类型定义扩展模板类
【发布时间】:2023-03-27 00:15:01
【问题描述】:

我模仿std::enable_shared_from_this创建了一个模板类,但是我让这个类在它的子类中使用了类型定义。 很遗憾! 虽然我用的是typename,但是编译后,

// 
// https://ideone.com/eYCBHW  http://ideone.com/eYCBHW
#include <iostream>
#include <set>
#include <map>
using namespace std;

template<class _S> struct A {
};

template<class _Subclass>
class Global {
public:
    typedef typename _Subclass::connection_t connection_t;
    //std::map<std::string, _Subclass::connection_t> connections;
    //std::set<_Subclass::connection_t> connections;
    //using typename _Subclass::connection_t;
    //typename _Subclass::connection_t* connections;
    //connection_t* connections;
};

class CConnection {};

class SConnection;

class Client : public Global<Client> {
public:
    typedef CConnection connection_t;
};

#if 0
class Server : public Global<Server> {
public:
    typedef SConnection connection_t;
};
#endif

class SConnection {};

int main() {
    // your code goes here
    return 0;
}

GCC 投诉:

prog.cpp: In instantiation of ‘class Global<Client>’:
prog.cpp:25:23:   required from here
prog.cpp:14:43: error: invalid use of incomplete type ‘class Client’
  typedef typename _Subclass::connection_t connection_t;
                                           ^~~~~~~~~~~~
prog.cpp:25:7: note: forward declaration of ‘class Client’
 class Client : public Global<Client> {
       ^~~~~~

如何解决?

参考文献

【问题讨论】:

  • 请注意,以下划线开头后跟一个大写字母(_S_Subclass!)的标识符以及包含两个后续下划线的标识符是为实现(即编译器)保留的。
  • 不幸的是,C++ 不能以这种方式工作。在首先定义其超类之前,无法定义一个类。因此,您不能使用 CRTP 设计模式从超类引用子类中定义的内容。没有解决办法。你必须以某种方式重组事物。
  • 你能解释一下你想要达到的目标吗?从代码中我不清楚...从我收集的信息来看,您想要的是 CRTP...
  • @SamVarshavchik 您可以从父类中引用子类中定义的内容,这就是 CRTP 的重点。但你只能在超类成员函数中做到这一点。
  • template&lt;class Subclass, class Connection&gt; class Global 怎么样?

标签: c++ templates


【解决方案1】:

在类级别拥有typedef 要求模板参数是完整类型。如果作为参数提供的类型实际上有一些等效的 typedef 本身,编译器将如何检查?

类似地,以下内容将失败:

class C;
using G = Global<C>; // C is not a complete type!
class C // too late...
{
    // ...
};

curiously recurring template pattern 的问题,这是您尝试实现的,在您尝试派生时,该类尚未完成,就像我上面的示例一样:

class Client : public Global<Client> // client is not yet complete!
{
}; // only HERE, it will get complete, but that's too late for above

有没有想过,为什么成员变量在成员函数中是已知的,即使是在函数之后声明的?那是因为

class C
{
    void f() { n = 12; }
    int n = 10;
};

编译时就像写成这样:

class C
{
    inline void f();
    int n = 10;
};

void C::f() { n = 12; } // n is known now!

这同时也是提示在哪里您可以按照您的意图使用模板参数:

template<class T> // different name used! *)
class Global
{
public:
    void f()
    {
        typedef typename T::connection_t connection_t; // possible here!
        // (similar to why you can use the static cast as in the link provided)
    }
};

不过,这对您的成员没有帮助:

std::map<std::string, typename T::connection_t> connections;
//                     ^ additionally was missing, but won't help either

T 此时仍不完整。

不过,在 cmets 中,您似乎只使用连接类型。如果您因为 typedef 以外的任何原因不需要客户端或服务器类,则可以非常简单地解决问题:

template<class T> // different name used! *)
class Global
{
    std::map<std::string, T> connections;
    //                    ^  use T directly
};

class Client : public Global<CConnection>
//                             ^ make sure it is defined BEFORE
{
    // ...
};

否则,您需要回退到其他方式,例如。 G。 pimplpattern,你可以让实现类从模板继承。

*) 标识符以下划线开头,后跟大写字母,以及包含两个后续标识符的标识符,保留用于实现(即供编译器使用)。定义自己的此类会产生未定义的行为。


编辑(从 cmets 窃取):

如果您需要来自Global 的客户端或服务器,您也可以将两者作为单独的 模板参数提供:

template <typename Base, typename Connection>
{
    // use Connection directly, e. g. for member definitions
    // and Base within member functions as mandated by CRTP
};

class Client : public Global<Client, CConnection>
{ /* ... */ };

【讨论】:

  • 好!我必须这样做,添加一个模板参数
猜你喜欢
  • 2023-01-03
  • 2010-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-16
  • 2013-01-23
  • 1970-01-01
相关资源
最近更新 更多