【问题标题】:Add accessor method without template instantiation添加没有模板实例化的访问器方法
【发布时间】:2017-12-21 11:14:24
【问题描述】:

我有一个由模板类型组成的结构,它可以处理不完整的类型,除非实例化,例如std::vector。使用类型安全索引包装器访问这些类型。示例:

template<class T>
struct Idx{unsinged val;};

struct Holder{
MyVector<Foo> foos;
MyVector<Bar> bars;
};

const Foo& foo = holder.foos.get(Idx<Foo>(...));

这很好用:Idx 不需要知道模板参数的类型,因为它从未使用过。 Holder 也适用于 Foo/Bar 的前向声明。我不能将Idx&lt;Foo&gt;Idx&lt;Bar&gt; 混淆,因为我只能用它们获得相应的类型。因为这太独特了,所以我想为持有者添加便利功能:

struct Holder{
MyVector<Foo> foos;
MyVector<Bar> bars;
const Foo& get(Idx<Foo> idx) { return foos.get(idx);}
const Bar& get(Idx<Bar> idx) { return bars.get(idx);}
};

但现在我需要 Holder 的完整类型,而我想避免这种情况。是否可以使用不完整类型和便利功能?也许一些模板会有所帮助,但我需要一些调度到最有可能实例化它的 foos 或 bar。

【问题讨论】:

  • 我打电话给XY Problem。我认为您的要求是其他错误设计决策的结果。让“类型安全索引包装器”Holder 同时负责多个集合类型是很奇怪的,而不是将类型安全索引包装器设计为一个通用向量/索引的集中功能,然后多次使用它。
  • 让我澄清一下:索引包装器称为Idx,它可以以类型安全的方式从集合MyVector 中获取实例。 Holder 是不同类型的 MyVector 的集合。 FooBar 可能不相关但经常一起使用或相关(Foo 包含 Idx&lt;Bar&gt;)还有另一种解决方案:让 Foo/Bar 从 Base 继承,将它们作为 Base-Ptrs 存储在单个容器中并使用Idx 中的类型将它们静态转换为它们的类型。但我不想那样。我为每个 Foo 实例命名以获取它的索引,并且使用它需要所有 Foo/Bar 实例的唯一名称。

标签: c++ templates


【解决方案1】:

也许你可以创建一个模板函数

template<typename T>
const T& getByIndex(const MyVector<T>& vec, const Idx<T>& idx)
{ return vec.get(idx); }

那你就可以在Holderlike里面使用了

const Foo& get(const Idx<Foo>& idx) { return getByIndex(foos, idx); }

不要期望参数是实例Idx&lt;Foo&gt;,因为这需要完全定义的类型(引用允许不完整的类型)。

【讨论】:

  • 不起作用。 get 函数实例化 getByIndex 实例化 MyVector -> 需要完整的 T
  • @Flamefire 它刚刚被点击...你需要将Idx&lt;Foo&gt; 替换为const Idx&lt;Foo&gt;&amp;,那么它只是一个引用类型,不完整的类型是可以接受的。也许你甚至不需要额外的getByIndex 重定向。
  • 不。问题不是不完整的Idx&lt;Foo&gt; 问题是不完整的Foo。我需要一些间接,所以我可以有 `const T& get(Idx idx){ return getContainer.get(idx); }
  • @Flamefire 据我所知,您试图在Helper 实现的范围内保持 Foo/Bar 未定义,而没有任何合理的理由。 Helper 不是通用的,因此它的实现应该知道FooBar 背后的实际类定义。
  • 并非如此。 Holder 只是 Foos 和 Bars 的容器。这是相当通用的。不完整类型的基本原理:Holder 可以被许多类访问。并非所有人都需要 Foo Bar,甚至不需要其中之一。如果 Foo/Bar 很复杂,它将不必要地增加那些根本不需要它们(或其中之一)的类的编译时间。因此,如果一个类需要 Foo,它会包含 Foo.h 并访问它。如果不是,Foo 只是一个简单的 fwd 声明结构。
【解决方案2】:

我在@grek40 的讨论中找到了解决方案:

我需要一个模板化的 get 函数,这样容器的 get 函数就不会在标头中实例化。因此我们有:

template&lt;class T&gt; const T&amp; get(DescIdx&lt;T&gt; idx) const { return getContainer&lt;T&gt;().get(idx); }

现在我们需要一个简单的getContainer 函数:template&lt;class T&gt; const MyVector&lt;T&gt;&amp; getContainer() const;

这需要为我们的容器实例化,例如:template&lt;&gt; inline const MyVector&lt;Foo&gt;&amp; Holder::getContainer() const { return foos; }

总而言之:

template<class T>
struct Idx{unsinged val;};

struct Holder{
MyVector<Foo> foos;
MyVector<Bar> bars;
template<class T> const T& get(DescIdx<T> idx) const { return getContainer<T>().get(idx); }
template<class T> const MyVector<T>& getContainer() const;
};

template<> inline const MyVector<Foo>& Holder::getContainer() const { return foos; }
template<> inline const MyVector<Bar>& Holder::getContainer() const { return bars; }

// Somewhere else
const Foo& foo = holder.get(Idx<Foo>(...));

这可以通过使用模板元编程来改进。步骤:

  1. 定义所有类型(Foo、Bar)的 boost::mpl::list
  2. 创建一个模板化结构MetaHolder,其中包含1 MyVector&lt;T&gt;
  3. 让 Holder 继承自 MetaHolder 用每种类型实例化
  4. 实现 1(!) 只返回 MetaHolder&lt;T&gt;.container 的通用 getContainer 函数。

一切都会在编译时得到解决,因此没有运行时开销。虽然 Boost.MPL 可能会变得非常繁重,但不确定编译时间开销。

【讨论】:

    猜你喜欢
    • 2012-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-14
    • 1970-01-01
    • 2017-07-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多