【发布时间】:2019-11-30 16:32:46
【问题描述】:
我正在实现一个图形类,每个顶点都有一个不一定相同类型的标签。我希望用户能够提供任何标签(在编译时),而不需要 Graph 或 Vertex 知道类型是什么。为此,我使用了隐藏在 Label 类中的模板化多态性,以使 Labels 具有值语义。它就像一个魅力,相关代码是这样的(暂时忽略注释部分):
//Label.hpp:
#include <memory>
class Label {
public:
template<class T> Label(const T& name) : m_pName(new Name<T>(name)) {}
Label(const Label& other) : m_pName(other.m_pName->copy()) {}
// Label(const Label& other, size_t extraInfo) : m_pName(other.m_pName->copyAndAddInfo(extraInfo)) {}
bool operator==(const Label& other) const { return *m_pName == *other.m_pName; }
private:
struct NameBase {
public:
virtual ~NameBase() = default;
virtual NameBase* copy() const = 0;
// virtual NameBase* copyAndAddInfo(size_t info) const = 0;
virtual bool operator==(const NameBase& other) const = 0;
};
template<class T> struct Name : NameBase {
public:
Name(T name) : m_name(std::move(name)) {}
NameBase* copy() const override { return new Name<T>(m_name); }
// NameBase* copyAndAddInfo(size_t info) const override {
// return new Name<std::pair<T, size_t>>(std::make_pair(m_name, info));
// }
bool operator==(const NameBase& other) const override {
const auto pOtherCasted = dynamic_cast<const Name<T>*>(&other);
if(pOtherCasted == nullptr) return false;
return m_name == pOtherCasted->m_name;
}
private:
T m_name;
};
std::unique_ptr<NameBase> m_pName;
};
用户(又名我)的一个要求是能够创建图的不相交并集(他已经能够创建双图,图的并集(具有相同标签的顶点映射到相同的顶点) , ETC。)。希望新图的标签是旧标签和某个整数的对,表示标签来自哪个图(这也确保新标签都是不同的)。为此,我认为我可以使用 Label 类的注释部分,但是我的 g++17 编译器存在的问题是,当我用某种类型 T 定义第一个 Label 时,它会尝试实例化所有可以使用:
Name<T>, Name<std::pair<T, size_t>>, Name<std::pair<std::pair<T, size_t>, size_t>>, ...
尝试例如编译这个(只是一个例子,否则可以工作):
// testLabel.cpp:
#include "Label.hpp"
#include <vector>
#include <iostream>
int main() {
std::vector<Label> labels;
labels.emplace_back(5);
labels.emplace_back(2.1);
labels.emplace_back(std::make_pair(true, 2));
Label testLabel(std::make_pair(true, 2));
for(const auto& label : labels)
std::cout<<(label == testLabel)<<std::endl;
return 0;
}
编译只是冻结。 (我没有看到其他人收到的消息“超出了最大模板递归容量”,但它显然试图实例化所有内容)。我尝试将函数分离到另一个类中并仅显式初始化所需的模板,以欺骗编译器,但没有任何效果。
期望的行为(我不知道是否可能)是实例化使用的模板类(连同成员函数声明),但延迟定义成员函数,即只有当它们真正被调用时。比如我调用Label(3),应该有一个类Name<int>,但是函数
NameBase* Name<int>::copyAndAddInfo(size_t info) const;
只有在我调用它的时候才会被定义。 (因此,Name<std::pair<int, size_t>> 只会按需实例化)
感觉应该是可行的,因为编译器已经按需定义了模板函数。
一个想法应该是完全改变实现并使用变体,但是
- 我不想手动跟踪用户需要的类型,并且
- 我非常喜欢这种实现方法,并希望在更改之前先看看它的局限性。
有人对我如何解决这个问题有任何提示吗?
【问题讨论】:
-
您希望能够重复此构造吗?如,您是否希望能够拥有
Name<std::pair<std::pair<T, size_t>, size_t>。如果没有,您可以为std::pair<T, size_t>添加一个Name的部分规范(或者最好是自己的类型,否则,如果有人想用一对标记,则会出现意外行为)。 -
是的,理论上我希望能够任意采用许多不相交的联合(不是我真的要这样做)。但是,我认为这可能是明智的(当然,使用自定义结构,而不是一对)。也许编译器会选择使用通用模板类,只要用户调用相应的 copyAndAddInfo() 函数并在不再使用该函数时退回到专业化中。我会尝试一下,让你知道。谢谢:)
-
我不认为你想做的事情是可能的。如果您在任何地方都有
Name<T>,编译器将为Name<std::pair<T, size_t>>创建代码,因为它必须创建需要此类型的copyAndAddInfo方法。但是,它必须创建下一个递归等等。具体来说,其深度在编译时是未知的。例如,考虑以下代码:std::unique_ptr<NodeBase> ptr; for(auto i = 0; i < n; ++i) { ptr = ptr->copyAndAddInfo();},其中n是一些运行时输入。
标签: c++ templates instantiation