【问题标题】:Correct forward declaration of fully specialized template classes正确前向声明完全专业化的模板类
【发布时间】:2016-11-09 17:51:17
【问题描述】:

假设我有以下一堆文件:

Generic.h:复杂的模板类

#pragma once

template<typename K, typename V, template<typename Key, typename Value, typename ...> typename C>
struct GenericMap
{
    C<K, V> key;
};

Special.h:定义提到的模板类的完全专用版本,简化易用性。

#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.hSpecial.h

#include "Client.h"
#include "Special.h"

Client::Client()
{
    map["343"] = 2;
}

ma​​in.cpp:

#include <Client.h>

int main(int argc, char**args) {
    Client c;
    return 0;
}

GenericMap 表示没有前向声明的模板类。对于某些用户来说,SpecialMapGenericMap 的完全专用版本就足够了,为了便于使用,使用了 typedef

现在Client 在内部使用SpecialMap,但头文件应该只声明SpecialMap 的前向声明。

很遗憾,以下文件将无法编译。不知何故,张贴的前向声明就足够了。什么是正确的?

对于冗长的列表,我很抱歉,但这是我能想到的最小的无效示例。

【问题讨论】:

  • 顺便说一句:我不推荐#pragma once。使用#ifndef... #define... #endif 模式实现可移植性。
  • A typedef 没有声明特化。
  • 感谢您的提示。我的公司只使用 Visual Studio。不存在便携性问题。 :-)
  • @erip,你知道支持#pragma once的编译器吗?
  • @Aleph0 使用 #pragma once 应该适用于任何现代编译器。不应该吗?

标签: c++ templates forward-declaration class-template


【解决方案1】:

在 cmets 中,您澄清说您实际上并不是指 C++ 专业化。您只是在询问 typedef:

typedef GenericMap<std::string, int, std::map> SpecialMap;

这就是故事的结尾。这将SpecialMap 声明为typedef,一个类型别名。任何需要使用SpecialMap 的翻译单元都需要包含此类型定义。而且只有这个定义。没有其他事情需要做。它不需要以任何其他方式声明。它是一个别名。 typedef 别名及其基础类型的搜索/替换会产生完全相同的结果。在一个翻译单元中声明的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 模板类,这里只是前向声明的。

【讨论】:

  • 非常感谢。这真的解决并澄清了我的问题。我会将下面发布的代码包含在一个新的头文件SpecialMapFwd.h中。
  • 我现在正试图弄清楚当GenericMap 包含typedef typedef C&lt;K, V&gt; map_type 时我必须做什么。我怎么可能转发声明map_type
  • 你不能在没有定义主类的情况下转发声明类成员。
  • 是的。我想我必须接受这一点。非常感谢您的帮助。我真的获得了很多见解。
猜你喜欢
  • 1970-01-01
  • 2011-01-18
  • 1970-01-01
  • 1970-01-01
  • 2023-03-28
  • 2023-04-08
  • 2018-08-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多