在 class<std::unique_ptr<B>> 中强制模板函数为 T=B*

Posted

技术标签:

【中文标题】在 class<std::unique_ptr<B>> 中强制模板函数为 T=B*【英文标题】:Enforce template function to be T=B* inside a class<std::unique_ptr<B>> 【发布时间】:2016-09-27 04:40:25 【问题描述】:

我有一个存储类,它可以添加/删除元素。

它的大多数公共函数共享一个非常相似的签名,如下所示:-

template<class T> class Storage
    public: void add(T& t) ... 
    //... other fields,functions

很棒的是T 可以是值或原始指针。

现在我想升级这个存储以支持T=std::unique_ptr。 这就是我想要完成的:-

Storage<std::unique_ptr<B>> store;
B* b = new B();
store.add(b);   //my attempt - it is currently not supported

这是我支持新功能的草稿:-

template<class T> class Storage
    public: template<class TWeak> void add(TWeak& tw)  ...   
    //... other fields,functions

从草案来看,我认为使用TWeak 作为模板参数有点危险——TWeak 可以是任何东西。

粗略地说,TWeak 只能是 T's weakpointer 与我的意图相矛盾。

更具体地说,我想强制执行这条规则:-

When   T=std::unique_ptr<B>   ==>    TWeak have to be B* or std::unique_ptr<B>
When   T=B*                   ==>    TWeak have to be B*
When   T=B                    ==>    TWeak have to be B

如何优雅地执行规则? 具有 2 个add 函数的解决方案仍然可以接受。

【问题讨论】:

unique_ptr 应该添加什么?假设所有权?如果是这样,为什么不创建一个 add(T&&) 函数呢? @rubenvb 是的,假设所有权。创建一个函数add(T&amp;&amp;)是可以的,但是我认为它并不能解决规则执行的问题。 首先,问问自己:为什么要制定这些规则?然后,看看它是否可以正常工作。然后,检查std::vector 的实现,看看它是如何处理这些事情的。如果这不能解决问题,请使用std::is_same 添加一些static_asserts 以​​强制执行带有明确错误消息的规则。不过,这些转换可能已经由编译器强制执行。 @rubenvb (1) 当我检查std::vector 时,我发现限制***.com/questions/15125631/… 因此,我认为std::vector 不是一个很好的例子。 .... (2) 关于static_assert,我的天赋不够。 “Ami Tavory”试图做到这一点(下面的答案),但没有完成,因为它很麻烦。 ... (3)关于“已经强制执行”,如果我自己也能控制的话,我会更高兴。我不喜欢关于模板的不可读的编译器错误。 通过原始指针删除 unique_ptr 意味着可能还有其他指向已删除对象的原始指针,这与您对该对象所有权的任何保证相矛盾。这是灾难的秘诀。我强烈建议你重新考虑你在做什么。 【参考方案1】:

为了保持界面简单,您可以扩展 Storage 的特化,它提供封闭类型,如 unique_ptr

template<class T>
class Storage<std::unique_ptr<T>> : public Storage<T*>
  public: using Storage<T*>::add;
  public: void add(std::unique_ptr<T>& t) ...   // 
;

我们继承,Storage&lt;T*&gt; 因为:

    根据您的要求,T*unique_ptr&lt;T&gt; 配合得很好 问题 它包括所有常用方法。像add()这样的特殊方法 单独定义,using 指令用于取消隐藏 基础方法

用法:

Storage<int> si; si.add(/*int variable*/);
Storage<int*> spi; spi.add(/*int* variable*/);
Storage<std::unique_ptr<int>> su; su.add(/*int* or unique_ptr<int> variable*/);

这是demo。

【讨论】:

哇!我不知道我可以这样推导出它。编译器如何知道选择哪一个 - Storage&lt;T&gt; vs Storage&lt;std::unique_ptr&lt;T&gt;&gt;?什么是技术(相同的类但具有不同的&lt;&gt;)? ...模板专业化? (即我在哪里可以找到更多信息?) @javaLover,实际上编译器有自己的方式来寻找模板专业化的最佳匹配。在这种情况下,它是直截了当的。您正在传递unique_ptr&lt;X&gt;,因此选择了相同的类专业化。如果您想传递shared_ptr 或其他包装器而不是unique_ptr,那么还有一种方法。我们需要稍微修改专门的类定义。我会更新我的答案。【参考方案2】:

更具体地说,我想强制执行这条规则:-

T=std::unique_ptr&lt;B&gt; ==> TWeak 必须是 B* 或 std::unique_ptr

T=B* ==> TWeak 必须是B*

T=B ==> TWeak 必须是B

您可以编写一个具有特化的“执行器”类,如下所示:

#include <type_traits>
#include <memory>

template<typename T, typename B, typename Tweak>
class tweak_enforcer
   
    static_assert(std::is_same<Tweak, B>::value, "must be of same type");
;

template<typename B, typename Tweak>
class tweak_enforcer<std::unique_ptr<B>, B, Tweak>

    static_assert(std::is_same<Tweak, B*>::value || std::is_same<Tweak, std::unique_ptr<B>>::value, "must be of pointer type");
;

每个特化使用static_assert 通过std::is_same 检查一个或多个预期类型匹配。

然后您可以像这样使用它(取消注释任何已注释的行会导致构建失败并显示适当的消息):

int main() 
     tweak_enforcer<int, int, int> dummy; 
    //  tweak_enforcer<int *, int *, int> dummy; 
     tweak_enforcer<int *, int *, int *> dummy; 
     tweak_enforcer<std::unique_ptr<int>, int, int *> dummy; 
     tweak_enforcer<std::unique_ptr<int>, int, std::unique_ptr<int>> dummy; 
    //  tweak_enforcer<std::unique_ptr<int>, int, std::shared_ptr<int>> dummy; 
   

【讨论】:

我注意到tweak_enforcer 需要3 个参数。可以只有两个吗?比如tweak_enforcer&lt;std::unique_ptr&lt;int&gt;, int*&gt;?例如,在类的范围内,我知道TWeak=int*T=std::unique_ptr&lt;int&gt;,但我不知道如何“访问”int @javaLover 如果我理解正确,我同意;开始将其编写为一个特征类,它接受两个参数,并将type 定义为它应该是什么。问题是,对应不是 1:1。您有一种情况,其中 1 种类型匹配 2 种可接受的类型。我知道这样做的唯一方法是将type 定义为某种类型列表,这非常麻烦。

以上是关于在 class<std::unique_ptr<B>> 中强制模板函数为 T=B*的主要内容,如果未能解决你的问题,请参考以下文章

如果“struct”和“class”是同一个东西,那么为啥在 template<class T> 中只使用“class”而不是“struct”?

C++,推入 List<Class*>,vector<Class> 迭代器

Qt 的编译警告 - 仅限 Mac OS X:<class> 已经是 <class> 的朋友

class在HTML里面有啥作用

CSS里面选择一个class里的a标签

java class <?>