gpt4 book ai didi

c++ - 将具有派生模板的对象传递给接受具有基本模板的对象的函数

转载 作者:行者123 更新时间:2023-12-01 14:11:19 25 4
gpt4 key购买 nike

如何将具有派生模板实例化的对象传递给接受具有基本模板实例化的对象的方法?

这似乎是可能的,因为 std::shared_ptr 或 std::pair 似乎有能力做到这一点。

例如

#pragma once

#include <iostream>
#include <memory>

struct Base {
virtual void print() = 0;
};

struct Derived : public Base {
void print() {
std::cout << "Got it!" << std::endl;
}
};

void printBase(const std::shared_ptr<Base> &ptr){
ptr->print();
}

void printBase(const std::pair<Base&, Base&> &pr){
pr.first.print();
}

template <typename T>
struct Wrap {
T& t;
};

void printBase(const Wrap<Base> &wrap) {
wrap.t.print();
}

int main() {
Derived d;
std::shared_ptr<Derived> ptr = std::make_shared<Derived>(d);
printBase(ptr); // works
std::pair<Derived&, Derived&> pr = {d, d};
printBase(pr); // works
Wrap<Derived> w = Wrap<Derived>{d};
// printBase(w); // gives compile error
}

最佳答案

您需要将转换构造函数和/或赋值运算符显式添加到 Wrapped type 以便能够从不同的类型进行转换。

这就是 std::shared_ptrstd::pair在内部执行此操作; shared_ptr<T>可以从 shared_ptr<U> 构建类型(具有 U* 可转换为 T* 的 SFINAE 限制)和 pair<T,U>可以从 pair<T2,U2> 构建类型(SFINAE 限制 T2 可转换为 TU2 可转换为 U)。

这样做就像添加一个新的构造函数一样简单:

template <typename T>
struct Wrap
{
Wrap(T& ref)
: t{ref}
{
}

template <typename U, typename = std::enable_if_t<std::is_convertible_v<U&, T&>>>
Wrap(const Wrap<U>& other)
: t{other.t}
{
}

T& t;
};

上面的例子使用is_convertible作为 enable_if 的条件所以构造函数只有在引用 U 时才可见可以转换为 T 的引用.这将限制它使得 U必须与 T 分层相关(因为引用在其他情况下不可转换)——这将允许 Wrapped<Derived>转换为 Wrapped<Base> ,但反之则不然。


编辑: 正如评论中提到的,值得注意的是,与属于层次结构一部分的类型不同——其中对 Derived 的引用可以作为对 Base 的引用传递,包装层次结构的类型将无法引用传递给 Template<Derived>作为一个引用 Template<Base> .

带有 std::shared_ptr<Derived> 的示例被传递给 const std::shared_ptr<Base>&由于 const 才真正起作用-C++ 中的终身扩展。这实际上并没有将它作为引用传递——而是将 std::shared_ptr<Base> 的临时对象具体化。它被传递给引用。它实际上与按值传递相同。

这也意味着您不能拥有 Template<Derived>传递给非 const Template<Base>引用,因为生命周期延长只发生在 const引用文献。


编辑:如评论中所述:

不需要使构造函数进行复制转换;它可以很容易地成为一个 R 值构造函数。但是,如果您正在对包装类型的销毁进行清理,那么您将需要以某种方式标记不需要清理已移动对象。最简单的方法是使用指针,重新绑定(bind)到 nullptr搬家后:

template <typename T>
struct Wrap
{
Wrap(T& ref)
: t{std::addressof(ref)}
{
}

Wrap(Wrap&& other)
: t{other.t}
{
other.t = nullptr;
}

Wrap(const Wrap&) = delete;

template <typename U, typename = std::enable_if_t<std::is_convertible_v<U&, T&>>>
Wrap(Wrap<U>&& other)
: t{other.t}
{
other.t = nullptr;
}

~Wrap() {
if (t != nullptr) {
cleanup(*t); // some cleanup code
}
}

T* t;
};

如果您想要的 API 无法使用指针,那么您可能需要使用 bool needs_cleanup在移动过程中得到适当的设置,因为引用不能被反弹。

注意:如果数据是private而不是 public如本例所示,您可能需要 friend声明:

template <typename> friend class Wrap;

所以 Wrap<T>可以访问 Wrap<U> 的私有(private)数据成员.

关于c++ - 将具有派生模板的对象传递给接受具有基本模板的对象的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62780506/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com