c++ - 使用子类中的类型定义扩展模板类
问题描述
我模仿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> {
^~~~~~
如何解决?
参考
解决方案
具有typedef
类级别要求模板参数是完整类型。如果作为参数提供的类型实际上具有一些等效的 typedef 本身,编译器将如何检查?
类似地,以下将失败:
class C;
using G = Global<C>; // C is not a complete type!
class C // too late...
{
// ...
};
奇怪地重复出现的模板模式的问题,这是您尝试实现的,在您尝试派生时,该类尚未完成,就像我上面的示例一样:
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
{
// ...
};
否则,您需要回退到其他方式,例如pimpl 模式,您可以让实现类从模板继承。
*) 标识符以下划线开头,后跟大写字母,以及包含两个后续标识符的标识符,保留用于实现(即供编译器使用)。定义自己的此类会产生未定义的行为。
编辑(从评论中窃取):
如果您需要来自内部的客户端或服务器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>
{ /* ... */ };
推荐阅读
- php - PHP Get Variable 避免在 URL 中删除 &
- javascript - 使用 Chessboard.js 加载 pgn 并允许用户按箭头键玩游戏
- ssis-2012 - SSIS CPU 使用率 100%
- flutter - 列表
> 已弃用 - ruby-on-rails - 比较相等的字符串时,RSpec 期望失败
- mongodb - 基于另一个集合的猫鼬过滤器集合
- angular - Angular:向标签添加点击方法 - 字符串中的属性绑定
- python - 我在客户端和服务器上有证书和私钥,但 gRPC 安全连接失败
- selenium - Selenium webdriver 找不到 xpath
- sockets - 如何从网络服务器检查套接字?