使用子类中的类型定义扩展模板类

时间:2019-08-18 17:11:21

标签: c++ templates

我模仿了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;
}

海湾合作委员会抱怨:

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> {
       ^~~~~~

如何解决?

参考

1 个答案:

答案 0 :(得分: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仍不完整。

但是,在注释中,您似乎只使用了连接类型。如果除了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。 pimpl pattern,您可以在其中让实现类从模板继承。

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


编辑(从评论中窃取):

如果您需要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>
{ /* ... */ };