正确的完全专业化模板的前向声明 类
Correct forward declaration of fully specialized template classes
假设我有以下一堆文件:
Generic.h: 复杂模板 class
#pragma once
template<typename K, typename V, template<typename Key, typename Value, typename ...> typename C>
struct GenericMap
{
C<K, V> key;
};
Special.h: 定义上述模板的完全专用版本 class,简化易用性。
#pragma once
#include "Generic.h"
#include <string>
#include <map>
typedef GenericMap<std::string, int, std::map> SpecialMap;
Client.h: 使用 SpecialMap
并定义前向声明的客户端。
#pragma once
class SpecialMap; // Wrong forward declaration
struct Client {
Client();
SpecialMap* map;
};
Client.cpp: 客户端代码可能知道 Generic.h
和 Special.h
#include "Client.h"
#include "Special.h"
Client::Client()
{
map["343"] = 2;
}
main.cpp:
#include <Client.h>
int main(int argc, char**args) {
Client c;
return 0;
}
GenericMap
表示没有前向声明的模板 class。对于某些用户来说,SpecialMap
的 GenericMap
完全专业化的版本就足够了,为了便于使用,使用了 typedef
。
现在Client
在内部使用SpecialMap
,但是头文件应该只为SpecialMap
声明一个前向声明。
不幸的是,以下文件将无法编译。以某种方式发布的前向声明就足够了。正确的是什么?
很抱歉列表太长,但这是我能想到的最小的非工作示例。
在评论中,您澄清说您实际上并不是指 C++ 专业化。您只是在询问 typedef:
typedef GenericMap<std::string, int, std::map> SpecialMap;
故事到此就差不多结束了。这将 SpecialMap
声明为 typedef
,一个类型别名。任何需要使用 SpecialMap
的翻译单元都需要包含这个类型定义。而且只有这个定义。不需要做任何其他事情。它不需要以任何其他方式声明。它是一个别名。 typedef
别名的 search/replace 及其基础类型产生完全相同的结果。在一个翻译单元中声明的 typedef
仅在该翻译单元中可见。其他翻译单位没有捷径可将 typedef
导入其范围。
在你的 Client.h:
#include <Special.h>
这是您定义此 typedef
的地方,这是引入此定义的唯一方法。
然而,这也可能是 typedef
是较大头文件的一部分的情况,并且希望单独引入 typedef。这可以通过仅包含以下内容的头文件来完成:
#include <string>
#include <map>
template<typename K, typename V,
template<typename Key, typename Value, typename ...>
typename C> struct GenericMap;
typedef GenericMap<std::string, int, std::map> SpecialMap;
这将是定义 typedef
别名所需的最低要求。任何实际需要使用它的东西,不仅需要 #include
这个头文件,还需要你的 Generic.h
头文件,它实际上定义了 GenericMap
模板 class,这只是在此转发声明。
假设我有以下一堆文件:
Generic.h: 复杂模板 class
#pragma once
template<typename K, typename V, template<typename Key, typename Value, typename ...> typename C>
struct GenericMap
{
C<K, V> key;
};
Special.h: 定义上述模板的完全专用版本 class,简化易用性。
#pragma once
#include "Generic.h"
#include <string>
#include <map>
typedef GenericMap<std::string, int, std::map> SpecialMap;
Client.h: 使用 SpecialMap
并定义前向声明的客户端。
#pragma once
class SpecialMap; // Wrong forward declaration
struct Client {
Client();
SpecialMap* map;
};
Client.cpp: 客户端代码可能知道 Generic.h
和 Special.h
#include "Client.h"
#include "Special.h"
Client::Client()
{
map["343"] = 2;
}
main.cpp:
#include <Client.h>
int main(int argc, char**args) {
Client c;
return 0;
}
GenericMap
表示没有前向声明的模板 class。对于某些用户来说,SpecialMap
的 GenericMap
完全专业化的版本就足够了,为了便于使用,使用了 typedef
。
现在Client
在内部使用SpecialMap
,但是头文件应该只为SpecialMap
声明一个前向声明。
不幸的是,以下文件将无法编译。以某种方式发布的前向声明就足够了。正确的是什么?
很抱歉列表太长,但这是我能想到的最小的非工作示例。
在评论中,您澄清说您实际上并不是指 C++ 专业化。您只是在询问 typedef:
typedef GenericMap<std::string, int, std::map> SpecialMap;
故事到此就差不多结束了。这将 SpecialMap
声明为 typedef
,一个类型别名。任何需要使用 SpecialMap
的翻译单元都需要包含这个类型定义。而且只有这个定义。不需要做任何其他事情。它不需要以任何其他方式声明。它是一个别名。 typedef
别名的 search/replace 及其基础类型产生完全相同的结果。在一个翻译单元中声明的 typedef
仅在该翻译单元中可见。其他翻译单位没有捷径可将 typedef
导入其范围。
在你的 Client.h:
#include <Special.h>
这是您定义此 typedef
的地方,这是引入此定义的唯一方法。
然而,这也可能是 typedef
是较大头文件的一部分的情况,并且希望单独引入 typedef。这可以通过仅包含以下内容的头文件来完成:
#include <string>
#include <map>
template<typename K, typename V,
template<typename Key, typename Value, typename ...>
typename C> struct GenericMap;
typedef GenericMap<std::string, int, std::map> SpecialMap;
这将是定义 typedef
别名所需的最低要求。任何实际需要使用它的东西,不仅需要 #include
这个头文件,还需要你的 Generic.h
头文件,它实际上定义了 GenericMap
模板 class,这只是在此转发声明。