【问题标题】:correctly allow polymorphic behavior in wrappers正确允许包装器中的多态行为
【发布时间】:2021-06-23 06:26:59
【问题描述】:

给定某种类型 T 的模板化包装器

template<typename T>
struct Wrapper {
  T* value;
};

如果我想为包装器提供一些多态行为,我通常会使用一些带有 sfinae 的模板化运算符。例如演员:

template<typename Base>
template<typename Derived, enable_if_t<is_base_of<T,From>::value,int> = 0>
Wrapper<Base>::Wrapper(Wrapper<Derived> & other) : value(other.value) {
}

它适用于简单的情况。然而,当我开始创建复杂的层次结构时,我发现编译器不理解发生了什么。一个非常愚蠢的代码是

#include<memory>
#include<iostream>

using namespace std;

struct Base {
    virtual ~Base() = default;
    int i;
};

struct Derived : Base {
    Derived(int in) {
        this->i = in;
    }
};

template<class T>
struct Wrapper {
  Wrapper(T & v) : value(&v) {}
  T* value;
};

template<typename T>
struct Interface {
    virtual ~Interface() = default;
    virtual T const & get() = 0;
};

template<typename T>
struct Something : Interface<T> {
    Something(Wrapper<T> const & value) : pt(&value) {}
    Wrapper<T>* pt;
    T const & get() {return pt->value;}
};

void foo(unique_ptr<Interface<Base>> && p) {
    std::cout << "I have interface to base" << p->get()->i;
}

int main() {
    Derived v(42);
    Wrapper<Derived> w(v);
    foo(make_unique<Something<Derived>>(w) );
}

在这种代码中,Something 派生自 Interface,Derived 从 Base,所以原则上应该可以从 unique_ptr&lt;Something&lt;Derived&gt;&gt; 转到 unique_ptr&lt;Interface&lt;Base&gt;&gt;,我相信这样做没有太大的危险。

您将使用 SFINAE/is_base_of 机制定义哪些运算符(ctors、assign、conversions 等)?在哪里?是否有一些一般规则可以遵循?

【问题讨论】:

  • 题外话:你的Something构造函数分配给pt指向value的指针,它可能是一个临时对象。这可能会导致在构造结束时出现悬空引用。
  • 对,应该是"Wrapper &"
  • 您的wrapper&lt;T&gt; 包含一个T,要应用多态性,您需要T 上的(智能)指针...否则您将进行对象切片。
  • 也对,我修改以清除问题。主要关注的是界面。主要我不希望将 Derived 转换为 Base,但说该接口也适用于 Base。

标签: c++ templates polymorphism wrapper


【解决方案1】:

一些东西从接口派生,从基础派生,所以原则上应该可以从unique_ptr&lt;Something&lt;Derived&gt;&gt;unique_ptr&lt;Interface&lt;Base&gt;&gt;

C++ 模板不是协变的。 Interface&lt;Derived&gt;Interface&lt;Base&gt; 之间没有关系。一般来说,它不是类型安全的。您的特定 Interface 类仅返回协变意义上的类型,但其他示例可以轻松接受该类型的参数,这将是逆变的。

在这两种不相关的类型上添加unique_ptrWrapper 并不会改变这种情况。您没有要保留的包装器(智能指针)类型的多态行为。

【讨论】:

  • 我并不是说它应该自动完成,但对于某些特定情况,应该通过提供一些特殊的模板化运算符(如 ctors、转换等)来允许转换。STL 这样做一直以来,例如允许从 unique_ptr 构建 unique_ptr
  • @Teloze:是的,包装器可以传递相关类型的多态使用。 这不会在包装器类型之间创建关系,即使它们在某种程度上是相关的。 而且因为Interface&lt;Base&gt;Interface&lt;Derived&gt; 是不相关的,所以不会传递多态行为。失败发生在Interface,而不是unique_ptrWrapper
  • 如果只在Base test() { return std::declval&lt;Derived&gt;(); } 格式正确的情况下采取适当的预防措施来允许此功能,那么定义template&lt;Base&gt;template&lt;Derived&gt;Interface&lt;Base&gt;::Interface(Interface&lt;Derived&gt; const &amp;) 有什么问题?这就是接口所做的。
  • @Telzone:我没有说这样做是错误的,我说它不会使类型相关。敞篷车比相关的要弱得多。可转换意味着给定 T 的实例,您可以获得一个新的不同对象,它是 U 的实例。相关要求 T 的实例本身就是 U 的实例,不涉及新对象。当您将相关类型 T 和 U 包装在 Interface 中时,您会得到 Interface&lt;T&gt; 可转换为 Interface&lt;U&gt; 但它们是不相关的。
  • 所以,如果我理解得很好,那原则上可以防止向上转换,我应该从 Interface 到 Interface的转换创建一个新的 unique_ptr> >
猜你喜欢
  • 2014-05-18
  • 2011-02-11
  • 2012-12-26
  • 1970-01-01
  • 1970-01-01
  • 2021-01-24
  • 2022-10-24
  • 1970-01-01
  • 2016-06-17
相关资源
最近更新 更多